[gnome-video-arcade] Add music clips to the Properties window.



commit e476b11bc134f86e08a6cadae193c198ac94bfcf
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sun Feb 14 19:41:17 2010 -0500

    Add music clips to the Properties window.
    
    This adds an optional dependency on GStreamer.  Music clips are streamed
    in MP3 format from http://www.arcade-history.com/.  Music clips are not
    yet available for all games, but the collection appears to be growing.

 NEWS                                           |   10 +
 README                                         |   30 +-
 configure.ac                                   |   19 +
 data/gnome-video-arcade.builder                |  146 +++++--
 data/gnome-video-arcade.schemas                |   19 +-
 docs/reference/Makefile.am                     |    3 +
 docs/reference/gnome-video-arcade-docs.sgml    |    1 +
 docs/reference/gnome-video-arcade-sections.txt |   32 ++-
 docs/reference/gnome-video-arcade.types        |    2 +
 docs/reference/tmpl/gva-history.sgml           |    9 +
 docs/reference/tmpl/gva-music-button.sgml      |  100 ++++
 docs/reference/tmpl/gva-preferences.sgml       |   16 +
 docs/reference/tmpl/gva-process.sgml           |    2 +-
 docs/reference/tmpl/gva-properties.sgml        |    3 +-
 docs/reference/tmpl/gva-ui.sgml                |    7 +
 docs/reference/tmpl/gva-util.sgml              |    1 +
 maint/Makefile.am                              |    5 +-
 maint/gva.xml                                  |    7 +
 po/POTFILES.in                                 |    1 +
 src/Makefile.am                                |    7 +-
 src/gva-common.h                               |   10 +-
 src/gva-db.c                                   |    2 +-
 src/gva-history.c                              |  116 +++--
 src/gva-history.h                              |    1 +
 src/gva-input-file.c                           |   12 +-
 src/gva-mame-process.c                         |   22 +-
 src/gva-music-button.c                         |  646 ++++++++++++++++++++++++
 src/gva-music-button.h                         |   91 ++++
 src/gva-preferences.c                          |   97 +++-
 src/gva-preferences.h                          |    2 +
 src/gva-process.c                              |   25 +-
 src/gva-properties.c                           |   67 +++-
 src/gva-properties.h                           |    3 +-
 src/gva-ui.c                                   |   29 +
 src/gva-ui.h                                   |    9 +
 src/gva-util.c                                 |   31 ++-
 src/gva-util.h                                 |   13 +-
 src/main.c                                     |    8 +
 38 files changed, 1456 insertions(+), 148 deletions(-)
---
diff --git a/NEWS b/NEWS
index f9e8eff..4817f5e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,13 @@
+GNOME Video Arcade 0.7.0
+========================
+
+        Released ???, 2010
+
+        What's New
+        ----------
+        * In the Properties window you can now listen to music clips of
+          the selected game.  Not all games have music clips available.
+
 GNOME Video Arcade 0.6.8
 ========================
 
diff --git a/README b/README
index ecaf53c..c9a9eaf 100644
--- a/README
+++ b/README
@@ -46,7 +46,7 @@ installed prior to compiling the source code:
        varies by distribution, but it should be something similar to
        gtk2-devel (Fedora) or libgtk2.0-dev (Debian/Ubuntu).
 
-   - Header files for GConf version 2.0 (or higher)
+   - Header files for GConf version 2.0 (or higher).
 
        GNOME Video Arcade uses GConf to remember preferences, favorites
        and other miscellaneous user data.  The GConf header files should
@@ -79,6 +79,18 @@ installed prior to compiling the source code:
        you can configure GNOME Video Arcade to not link against it.
        See the Installation section to find out how.
 
+   - Header files for GStreamer version 0.10 (or higher). (optional)
+
+       The GStreamer header files should be available from your
+       GNU/Linux distribution as a "development" package.  The exact
+       package name varies by distribution, but it should be something
+       similar to gstreamer-devel (Fedora) or libgstreamer0.10-dev
+       (Debian/Ubuntu).
+
+       GNOME Video Arcade uses GStreamer to stream video game music
+       clips in the Properties window.  The music clips are streamed
+       from http://www.arcade-history.com.
+
    - Header files for libgnomeui version 2.14 (or higher). (optional)
 
        The libgnomeui header files should be available from your
@@ -142,11 +154,11 @@ including GNOME Video Arcade.  This section supplements the INSTALL
 file with information specific to GNOME Video Arcade.
 
 These instructions are written specifically for GNOME Video Arcade
-version 0.6.7 and may change in forthcoming releases.
+version 0.7.0 and may change in forthcoming releases.
 
 The standard installation procedure looks like this:
 
-   $ cd /path/to/gnome-video-arcade-0.6.7
+   $ cd /path/to/gnome-video-arcade-0.7.0
    $ ./configure
    $ make
    $ su -c "make install"
@@ -193,11 +205,13 @@ installed, the documentation can be viewed through Devhelp.
 
 The --with-gnome and --without-gnome configure options tell "make"
 whether to link against high-level GNOME libraries like libgnomeui.
-Similarly, the --with-wnck and --without-wnck configure options
-tell "make" whether to link against libwnck, and the --with-dbus
-and --without-dbus configure options tell "make" whether to link
-against D-Bus.  See the previous section for details on what these
-libraries are used for.
+Similar "with"/"without" options are available for other optional
+libraries including D-Bus, GStreamer and libwnck.  See the previous
+section for details on what these libraries are used for.
+
+For a complete list of configure options, run:
+
+   $ ./configure --help
 
 UPDATE: --with-gnome is now ignored if GTK+ >= 2.14 is detected.
 
diff --git a/configure.ac b/configure.ac
index f7ec091..c5e792c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,24 @@ fi
 AC_SUBST(GNOME_CFLAGS)
 AC_SUBST(GNOME_LIBS)
 
+# --with-gstreamer=[yes|no]
+AC_ARG_WITH([gstreamer],
+        [AC_HELP_STRING([--with-gstreamer],
+        [use GStreamer to stream music [default=yes]])],
+        with_gstreamer="$withval", with_gstreamer="yes")
+if test "$with_gstreamer" = "yes"; then
+GSTREAMER_MODULES="gstreamer-0.10"
+PKG_CHECK_MODULES(GSTREAMER, $GSTREAMER_MODULES)
+AC_DEFINE_UNQUOTED(HAVE_GSTREAMER, 1,
+        [Define to 1 if you are using GStreamer])
+else
+GSTREAMER_CFLAGS=
+GSTREAMER_LIBS=
+fi
+AM_CONDITIONAL(HAVE_GSTREAMER, test $with_gstreamer = yes)
+AC_SUBST(GSTREAMER_CFLAGS)
+AC_SUBST(GSTREAMER_LIBS)
+
 # --with-wnck=[yes|no]
 AC_ARG_WITH([wnck],
         [AC_HELP_STRING([--with-wnck],
@@ -235,6 +253,7 @@ echo "    Category File   : $with_category"
 echo "    History File    : $with_history"
 echo "    NPlayers File   : $with_nplayers"
 echo "    Use D-Bus       : $with_dbus"
+echo "    Use GStreamer   : $with_gstreamer"
 echo "    Use libgnomeui  : $with_gnome"
 echo "    Use libwnck     : $with_wnck"
 echo
diff --git a/data/gnome-video-arcade.builder b/data/gnome-video-arcade.builder
index f79a8ec..6904b8e 100644
--- a/data/gnome-video-arcade.builder
+++ b/data/gnome-video-arcade.builder
@@ -1,6 +1,7 @@
 <?xml version="1.0"?>
 <interface>
   <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-requires gva 0.0 -->
   <!-- interface-naming-policy toplevel-contextual -->
   <object class="GtkWindow" id="main-window">
     <property name="title" translatable="yes">GNOME Video Arcade</property>
@@ -63,25 +64,28 @@
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="tooltip-text" translatable="yes">Show my search results</property>
+                        <property name="tooltip_text" translatable="yes">Show my search results</property>
                       </object>
                       <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
                         <property name="position">2</property>
                       </packing>
                     </child>
                   </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
                 </child>
                 <child>
                   <object class="GvaMuteButton" id="main-mute-button">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="receives_default">True</property>
                     <property name="has_tooltip">True</property>
                     <property name="tooltip_markup">In-game sound is enabled</property>
                     <property name="tooltip_text">In-game sound is enabled</property>
-                    <property name="relief">GTK_RELIEF_NONE</property>
-                    <child>
-                      <placeholder/>
-                    </child>
+                    <property name="relief">none</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
@@ -93,6 +97,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
+                <property name="position">0</property>
               </packing>
             </child>
             <child>
@@ -600,6 +605,7 @@
                           <packing>
                             <property name="expand">False</property>
                             <property name="fill">False</property>
+                            <property name="position">0</property>
                           </packing>
                         </child>
                         <child>
@@ -686,6 +692,7 @@
     <property name="default_height">400</property>
     <property name="destroy_with_parent">True</property>
     <property name="transient_for">main-window</property>
+    <signal name="hide" handler="gva_properties_hide_cb"/>
     <signal name="delete_event" handler="gtk_widget_hide_on_delete"/>
     <signal name="show" handler="gva_properties_show_cb"/>
     <child>
@@ -702,7 +709,7 @@
             <property name="n_columns">2</property>
             <property name="column_spacing">12</property>
             <child>
-              <object class="GtkHBox" id="properties-hbox">
+              <object class="GtkHBox" id="properties-upper-hbox">
                 <property name="visible">True</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="spacing">3</property>
@@ -790,7 +797,6 @@ Manufacturer, Year</property>
             <child>
               <object class="GtkNotebook" id="properties-history-notebook">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="show_tabs">False</property>
                 <property name="show_border">False</property>
@@ -842,7 +848,6 @@ Manufacturer, Year</property>
             <child>
               <object class="GtkNotebook" id="properties-gallery-notebook">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="show_tabs">False</property>
                 <property name="show_border">False</property>
@@ -956,7 +961,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-imperfect-color-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ The colors aren't 100% accurate.</property>
+                                                    <property name="label" translatable="yes">&#x2022; The colors aren't 100% accurate.</property>
                                                     <property name="ellipsize">end</property>
                                                   </object>
                                                   <packing>
@@ -967,7 +972,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-preliminary-color-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ The colors are completely wrong.</property>
+                                                    <property name="label" translatable="yes">&#x2022; The colors are completely wrong.</property>
                                                     <property name="ellipsize">end</property>
                                                   </object>
                                                   <packing>
@@ -978,7 +983,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-imperfect-graphic-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ The video emulation isn't 100% accurate.</property>
+                                                    <property name="label" translatable="yes">&#x2022; The video emulation isn't 100% accurate.</property>
                                                     <property name="ellipsize">end</property>
                                                   </object>
                                                   <packing>
@@ -989,7 +994,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-imperfect-sound-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ The sound emulation isn't 100% accurate.</property>
+                                                    <property name="label" translatable="yes">&#x2022; The sound emulation isn't 100% accurate.</property>
                                                     <property name="ellipsize">end</property>
                                                   </object>
                                                   <packing>
@@ -1000,7 +1005,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-preliminary-sound-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ The game lacks sound.</property>
+                                                    <property name="label" translatable="yes">&#x2022; The game lacks sound.</property>
                                                     <property name="ellipsize">end</property>
                                                   </object>
                                                   <packing>
@@ -1011,7 +1016,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-preliminary-cocktail-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ Screen flipping in cocktail mode is not supported.</property>
+                                                    <property name="label" translatable="yes">&#x2022; Screen flipping in cocktail mode is not supported.</property>
                                                     <property name="ellipsize">end</property>
                                                   </object>
                                                   <packing>
@@ -1022,7 +1027,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-preliminary-emulation-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ &lt;b&gt;THIS GAME DOESN'T WORK.&lt;/b&gt;</property>
+                                                    <property name="label" translatable="yes">&#x2022; &lt;b&gt;THIS GAME DOESN'T WORK.&lt;/b&gt;</property>
                                                     <property name="use_markup">True</property>
                                                   </object>
                                                   <packing>
@@ -1035,7 +1040,7 @@ Manufacturer, Year</property>
                                                   <object class="GtkLabel" id="properties-preliminary-protection-label">
                                                     <property name="visible">True</property>
                                                     <property name="xalign">0</property>
-                                                    <property name="label" translatable="yes">â?¢ The game has protection which isn't fully emulated.</property>
+                                                    <property name="label" translatable="yes">&#x2022; The game has protection which isn't fully emulated.</property>
                                                   </object>
                                                   <packing>
                                                     <property name="expand">False</property>
@@ -1493,29 +1498,106 @@ Manufacturer, Year</property>
           </packing>
         </child>
         <child>
-          <object class="GtkHButtonBox" id="properties-button-box">
+          <object class="GtkHBox" id="properties-lower-hbox">
             <property name="visible">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="layout_style">end</property>
+            <property name="spacing">12</property>
             <child>
-              <object class="GtkButton" id="properties-close-button">
-                <property name="label">gtk-close</property>
+              <object class="GtkTable" id="properties-music-table">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="has_focus">True</property>
-                <property name="is_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="has_default">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="tooltip_text" translatable="yes">Close this window</property>
-                <property name="use_stock">True</property>
-                <signal name="clicked" handler="gva_properties_close_clicked_cb" object="properties-window"/>
+                <property name="n_rows">2</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">6</property>
+                <child>
+                  <object class="GtkAlignment" id="properties-music-alignment">
+                    <property name="visible">True</property>
+                    <property name="yalign">1</property>
+                    <property name="yscale">0</property>
+                    <child>
+                      <object class="GvaMusicButton" id="properties-music-button">
+                        <property name="width_request">64</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="tooltip_text" translatable="yes">Play a music clip</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="properties-music-status">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">&lt;small&gt;&lt;/small&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="ellipsize">end</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="properties-music-auto-play">
+                    <property name="label">(auto-play) GtkUIManager supplies the label</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="properties-button-box-alignment">
+                <property name="visible">True</property>
+                <property name="yalign">1</property>
+                <property name="yscale">0</property>
+                <child>
+                  <object class="GtkHButtonBox" id="properties-button-box">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="layout_style">end</property>
+                    <child>
+                      <object class="GtkButton" id="properties-close-button">
+                        <property name="label">gtk-close</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="can_default">True</property>
+                        <property name="has_default">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="tooltip_text" translatable="yes">Close this window</property>
+                        <property name="use_stock">True</property>
+                        <signal name="clicked" handler="gtk_widget_hide" object="properties-window"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
               </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
-                <property name="position">0</property>
+                <property name="pack_type">end</property>
+                <property name="position">1</property>
               </packing>
             </child>
           </object>
diff --git a/data/gnome-video-arcade.schemas b/data/gnome-video-arcade.schemas
index 2e34d72..062f928 100644
--- a/data/gnome-video-arcade.schemas
+++ b/data/gnome-video-arcade.schemas
@@ -16,6 +16,23 @@
     </schema>
 
     <schema>
+      <applyto>/apps/gnome-video-arcade/auto-play</applyto>
+      <key>/schemas/apps/gnome-video-arcade/auto-play</key>
+      <owner>gnome-video-arcade</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>Auto-play music clip</short>
+        <long>Enable automatic playing of in-game music clips.
+        If true, and a music clip is available for the selected game,
+        the clip will automatically begin playing when the user opens the
+        Properties window or when the user selects a different game while
+        the Properties window is visible.  Music clips are streamed from
+        http://www.arcade-history.com/.</long>
+      </locale>
+    </schema>
+
+    <schema>
       <applyto>/apps/gnome-video-arcade/auto-save</applyto>
       <key>/schemas/apps/gnome-video-arcade/auto-save</key>
       <owner>gnome-video-arcade</owner>
@@ -177,7 +194,7 @@
 
     <schema>
       <applyto>/apps/gnome-video-arcade/sound-muted</applyto>
-      <key>/apps/gnome-video-arcade/sound-muted</key>
+      <key>/schemas/apps/gnome-video-arcade/sound-muted</key>
       <owner>gnome-video-arcade</owner>
       <type>bool</type>
       <default>false</default>
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index ddd9a2e..58f84df 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -81,6 +81,7 @@ INCLUDES=							\
 	$(GCONF_CFLAGS)						\
 	$(GLADE_CFLAGS)						\
 	$(GNOME_CFLAGS)						\
+	$(GSTREAMER_CFLAGS)					\
 	$(SQLITE_CFLAGS)					\
 	$(WNCK_CFLAGS)
 
@@ -105,6 +106,7 @@ GTKDOC_LIBS=							\
 	$(top_builddir)/src/gva-main.o				\
 	$(top_builddir)/src/gva-mame-common.o			\
 	$(top_builddir)/src/gva-mame-process.o			\
+	$(top_builddir)/src/gva-music-button.o			\
 	$(top_builddir)/src/gva-mute-button.o			\
 	$(top_builddir)/src/gva-nplayers.o			\
 	$(top_builddir)/src/gva-play-back.o			\
@@ -125,6 +127,7 @@ GTKDOC_LIBS=							\
 	$(GCONF_LIBS)						\
 	$(GLADE_LIBS)						\
 	$(GNOME_LIBS)						\
+	$(GSTREAMER_LIBS)					\
 	$(SQLITE_LIBS)						\
 	$(WNCK_LIBS)
 
diff --git a/docs/reference/gnome-video-arcade-docs.sgml b/docs/reference/gnome-video-arcade-docs.sgml
index e24ca55..f0503c8 100644
--- a/docs/reference/gnome-video-arcade-docs.sgml
+++ b/docs/reference/gnome-video-arcade-docs.sgml
@@ -45,6 +45,7 @@
     <xi:include href="xml/gva-input-file.xml"/>
     <xi:include href="xml/gva-link-button.xml"/>
     <xi:include href="xml/gva-mame-process.xml"/>
+    <xi:include href="xml/gva-music-button.xml"/>
     <xi:include href="xml/gva-mute-button.xml"/>
     <xi:include href="xml/gva-process.xml"/>
   </chapter>
diff --git a/docs/reference/gnome-video-arcade-sections.txt b/docs/reference/gnome-video-arcade-sections.txt
index 4847410..31b63ad 100644
--- a/docs/reference/gnome-video-arcade-sections.txt
+++ b/docs/reference/gnome-video-arcade-sections.txt
@@ -176,6 +176,7 @@ gva_link_button_get_type
 <FILE>gva-history</FILE>
 gva_history_init
 gva_history_lookup
+gva_history_lookup_id
 </SECTION>
 
 <SECTION>
@@ -263,6 +264,29 @@ gva_mame_process_get_type
 </SECTION>
 
 <SECTION>
+<FILE>gva-music-button</FILE>
+<TITLE>GvaMusicButton</TITLE>
+GvaMusicButton
+gva_music_button_new
+gva_music_button_play
+gva_music_button_pause
+gva_music_button_get_game
+gva_music_button_set_game
+gva_music_button_get_status
+<SUBSECTION Standard>
+GVA_MUSIC_BUTTON
+GVA_IS_MUSIC_BUTTON
+GVA_TYPE_MUSIC_BUTTON
+GVA_MUSIC_BUTTON_CLASS
+GVA_IS_MUSIC_BUTTON_CLASS
+GVA_MUSIC_BUTTON_GET_CLASS
+GvaMusicButtonClass
+<SUBSECTION Private>
+GvaMusicButtonPrivate
+gva_music_button_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gva-mute-button</FILE>
 <TITLE>GvaMuteButton</TITLE>
 GvaMuteButton
@@ -303,6 +327,8 @@ gva_play_back_window_hide_cb
 <SECTION>
 <FILE>gva-preferences</FILE>
 gva_preferences_init
+gva_preferences_get_auto_play
+gva_preferences_set_auto_play
 gva_preferences_get_auto_save
 gva_preferences_set_auto_save
 gva_preferences_get_full_screen
@@ -350,7 +376,7 @@ gva_process_get_type
 <FILE>gva-properties</FILE>
 gva_properties_init
 gva_properties_show_game
-gva_properties_close_clicked_cb
+gva_properties_hide_cb
 gva_properties_show_cb
 </SECTION>
 
@@ -386,6 +412,7 @@ gva_time_get_type
 <SECTION>
 <FILE>gva-ui</FILE>
 GVA_ACTION_ABOUT
+GVA_ACTION_AUTO_PLAY
 GVA_ACTION_AUTO_SAVE
 GVA_ACTION_CONTENTS
 GVA_ACTION_FULL_SCREEN
@@ -452,6 +479,9 @@ GVA_WIDGET_PROPERTIES_HISTORY_TEXT_VIEW
 GVA_WIDGET_PROPERTIES_IMPERFECT_COLOR_LABEL
 GVA_WIDGET_PROPERTIES_IMPERFECT_GRAPHIC_LABEL
 GVA_WIDGET_PROPERTIES_IMPERFECT_SOUND_LABEL
+GVA_WIDGET_PROPERTIES_MUSIC_AUTO_PLAY
+GVA_WIDGET_PROPERTIES_MUSIC_BUTTON
+GVA_WIDGET_PROPERTIES_MUSIC_STATUS
 GVA_WIDGET_PROPERTIES_NOTEBOOK
 GVA_WIDGET_PROPERTIES_ORIGINAL_LINKS
 GVA_WIDGET_PROPERTIES_ORIGINAL_VBOX
diff --git a/docs/reference/gnome-video-arcade.types b/docs/reference/gnome-video-arcade.types
index 1a62282..8811992 100644
--- a/docs/reference/gnome-video-arcade.types
+++ b/docs/reference/gnome-video-arcade.types
@@ -4,6 +4,7 @@
 #include <gva-input-file.h>
 #include <gva-link-button.h>
 #include <gva-mame-process.h>
+#include <gva-music-button.h>
 #include <gva-mute-button.h>
 #include <gva-process.h>
 #include <gva-time.h>
@@ -14,6 +15,7 @@ gva_game_store_get_type
 gva_input_file_get_type
 gva_link_button_get_type
 gva_mame_process_get_type
+gva_music_button_get_type
 gva_mute_button_get_type
 gva_process_get_type
 gva_time_get_type
diff --git a/docs/reference/tmpl/gva-history.sgml b/docs/reference/tmpl/gva-history.sgml
index b0b73f0..0e2839c 100644
--- a/docs/reference/tmpl/gva-history.sgml
+++ b/docs/reference/tmpl/gva-history.sgml
@@ -36,3 +36,12 @@
 @Returns: 
 
 
+<!-- ##### FUNCTION gva_history_lookup_id ##### -->
+<para>
+
+</para>
+
+ game: 
+ Returns: 
+
+
diff --git a/docs/reference/tmpl/gva-music-button.sgml b/docs/reference/tmpl/gva-music-button.sgml
new file mode 100644
index 0000000..bfad33c
--- /dev/null
+++ b/docs/reference/tmpl/gva-music-button.sgml
@@ -0,0 +1,100 @@
+<!-- ##### SECTION Title ##### -->
+GvaMusicButton
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GvaMusicButton ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SIGNAL GvaMusicButton::pause ##### -->
+<para>
+
+</para>
+
+ gvamusicbutton: the object which received the signal.
+
+<!-- ##### SIGNAL GvaMusicButton::play ##### -->
+<para>
+
+</para>
+
+ gvamusicbutton: the object which received the signal.
+
+<!-- ##### ARG GvaMusicButton:game ##### -->
+<para>
+
+</para>
+
+<!-- ##### ARG GvaMusicButton:status ##### -->
+<para>
+
+</para>
+
+<!-- ##### FUNCTION gva_music_button_new ##### -->
+<para>
+
+</para>
+
+ Returns: 
+
+
+<!-- ##### FUNCTION gva_music_button_play ##### -->
+<para>
+
+</para>
+
+ music_button: 
+
+
+<!-- ##### FUNCTION gva_music_button_pause ##### -->
+<para>
+
+</para>
+
+ music_button: 
+
+
+<!-- ##### FUNCTION gva_music_button_get_game ##### -->
+<para>
+
+</para>
+
+ music_button: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gva_music_button_set_game ##### -->
+<para>
+
+</para>
+
+ music_button: 
+ game: 
+
+
+<!-- ##### FUNCTION gva_music_button_get_status ##### -->
+<para>
+
+</para>
+
+ music_button: 
+ Returns: 
+
+
diff --git a/docs/reference/tmpl/gva-preferences.sgml b/docs/reference/tmpl/gva-preferences.sgml
index 2f86ebe..9a0c0d2 100644
--- a/docs/reference/tmpl/gva-preferences.sgml
+++ b/docs/reference/tmpl/gva-preferences.sgml
@@ -24,6 +24,22 @@
 
 
 
+<!-- ##### FUNCTION gva_preferences_get_auto_play ##### -->
+<para>
+
+</para>
+
+ Returns: 
+
+
+<!-- ##### FUNCTION gva_preferences_set_auto_play ##### -->
+<para>
+
+</para>
+
+ auto_play: 
+
+
 <!-- ##### FUNCTION gva_preferences_get_auto_save ##### -->
 <para>
 
diff --git a/docs/reference/tmpl/gva-process.sgml b/docs/reference/tmpl/gva-process.sgml
index b9fbfc1..b54af41 100644
--- a/docs/reference/tmpl/gva-process.sgml
+++ b/docs/reference/tmpl/gva-process.sgml
@@ -1,5 +1,5 @@
 <!-- ##### SECTION Title ##### -->
-
+GvaProcess
 
 <!-- ##### SECTION Short_Description ##### -->
 
diff --git a/docs/reference/tmpl/gva-properties.sgml b/docs/reference/tmpl/gva-properties.sgml
index f8213c6..b356e69 100644
--- a/docs/reference/tmpl/gva-properties.sgml
+++ b/docs/reference/tmpl/gva-properties.sgml
@@ -32,13 +32,12 @@
 @game: 
 
 
-<!-- ##### FUNCTION gva_properties_close_clicked_cb ##### -->
+<!-- ##### FUNCTION gva_properties_hide_cb ##### -->
 <para>
 
 </para>
 
 @window: 
- button: 
 
 
 <!-- ##### FUNCTION gva_properties_show_cb ##### -->
diff --git a/docs/reference/tmpl/gva-ui.sgml b/docs/reference/tmpl/gva-ui.sgml
index 8d1fa1f..35a3be4 100644
--- a/docs/reference/tmpl/gva-ui.sgml
+++ b/docs/reference/tmpl/gva-ui.sgml
@@ -24,6 +24,13 @@
 
 
 
+<!-- ##### MACRO GVA_ACTION_AUTO_PLAY ##### -->
+<para>
+
+</para>
+
+
+
 <!-- ##### MACRO GVA_ACTION_AUTO_SAVE ##### -->
 <para>
 
diff --git a/docs/reference/tmpl/gva-util.sgml b/docs/reference/tmpl/gva-util.sgml
index de07e35..a7d447d 100644
--- a/docs/reference/tmpl/gva-util.sgml
+++ b/docs/reference/tmpl/gva-util.sgml
@@ -53,6 +53,7 @@
 @GVA_DEBUG_SQL: 
 @GVA_DEBUG_IO: 
 @GVA_DEBUG_INP: 
+ GVA_DEBUG_GST: 
 
 <!-- ##### FUNCTION gva_get_last_version ##### -->
 <para>
diff --git a/maint/Makefile.am b/maint/Makefile.am
index ff7a80f..d573cd2 100644
--- a/maint/Makefile.am
+++ b/maint/Makefile.am
@@ -14,11 +14,12 @@ gladegvadir = `$(PKG_CONFIG) --variable=moduledir gladeui-1.0`
 
 libgladegva_la_CFLAGS = \
 	@GLIB_CFLAGS@ @GTK_CFLAGS@ \
-	@GCONF_CFLAGS@
+	@GCONF_CFLAGS@ @GSTREAMER_CFLAGS@
 
 libgladegva_la_SOURCES = \
 	$(top_srcdir)/src/gva-column-manager.c \
 	$(top_srcdir)/src/gva-link-button.c \
+	$(top_srcdir)/src/gva-music-button.c \
 	$(top_srcdir)/src/gva-mute-button.c
 
 libgladegva_la_LDFLAGS = \
@@ -26,7 +27,7 @@ libgladegva_la_LDFLAGS = \
 
 libgladegva_la_LIBADD = \
 	@GLIB_LIBS@ @GTK_LIBS@ \
-	@GCONF_LIBS@
+	@GCONF_LIBS@ @GSTREAMER_LIBS@
 
 EXTRA_DIST = \
 	$(gladecatalog_DATA) \
diff --git a/maint/gva.xml b/maint/gva.xml
index d79d652..14d46ba 100644
--- a/maint/gva.xml
+++ b/maint/gva.xml
@@ -10,6 +10,12 @@
 
     <glade-widget-class name="GvaLinkButton" generic-name="link-button" title="Link Button"/>
 
+    <glade-widget-class name="GvaMusicButton" generic-name="music-button" title="Music Button">
+      <properties>
+        <property id="game"/>
+      </properties>
+    </glade-widget-class>
+
     <glade-widget-class name="GvaMuteButton" generic-name="mute-button" title="Mute Button">
       <properties>
         <property id="muted"/>
@@ -21,6 +27,7 @@
   <glade-widget-group name="gva-widgets" title="GNOME Video Arcade">
     <glade-widget-class-ref name="GvaColumnManager"/>
     <glade-widget-class-ref name="GvaLinkButton"/>
+    <glade-widget-class-ref name="GvaMusicButton"/>
     <glade-widget-class-ref name="GvaMuteButton"/>
   </glade-widget-group>
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 765e4b7..5843b27 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -14,6 +14,7 @@ src/gva-mame-common.c
 src/gva-mame-process.c
 src/gva-mame-sdlmame.c
 src/gva-mame-xmame.c
+src/gva-music-button.c
 src/gva-mute-button.c
 src/gva-nplayers.c
 src/gva-play-back.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 4e8b72c..fae9659 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,7 +6,7 @@ AM_CFLAGS = \
 	-Wall \
 	@DBUS_CFLAGS@ @GLIB_CFLAGS@ @GTK_CFLAGS@ \
 	@GIT_CFLAGS@ @GCONF_CFLAGS@ @GNOME_CFLAGS@ \
-	@SQLITE_CFLAGS@ @WNCK_CFLAGS@
+	@GSTREAMER_CFLAGS@ @SQLITE_CFLAGS@ @WNCK_CFLAGS@
 
 # The DATADIR, LIBDIR, PREFIX, and SYSCONFDIR definitions
 # are only needed for libgnome.  GLib handles it better.
@@ -63,6 +63,8 @@ gnome_video_arcade_SOURCES = \
 	gva-mame-process.c		\
 	gva-mame-process.h		\
 	gva-mame.h			\
+	gva-music-button.c		\
+	gva-music-button.h		\
 	gva-mute-button.c		\
 	gva-mute-button.h		\
 	gva-nplayers.c			\
@@ -95,7 +97,8 @@ EXTRA_gnome_video_arcade_SOURCES = \
 gnome_video_arcade_LDADD = \
 	@DBUS_LIBS@ @GLIB_LIBS@ @GTK_LIBS@ \
 	@GIT_LIBS@ @GCONF_LIBS@ @GNOME_LIBS@ \
-	@MAME_BACKEND@ @SQLITE_LIBS@ @WNCK_LIBS@
+	@GSTREAMER_LIBS@ @MAME_BACKEND@ \
+	@SQLITE_LIBS@ @WNCK_LIBS@
 
 gnome_video_arcade_DEPENDENCIES = \
 	@MAME_BACKEND@
diff --git a/src/gva-common.h b/src/gva-common.h
index 44b7829..9b67cf6 100644
--- a/src/gva-common.h
+++ b/src/gva-common.h
@@ -19,6 +19,8 @@
 /**
  * SECTION: gva-common
  * @short_description: Common Definitions
+ *
+ * Common symbols used throughout GNOME Video Arcade.
  **/
 
 #ifndef GVA_COMMON_H
@@ -28,10 +30,13 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#include <glib.h>
+#include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
-#include <gtk/gtk.h>
+
+#if HAVE_GSTREAMER
+#include <gst/gst.h>
+#endif
 
 #include <sqlite3.h>
 #include <gconf/gconf-client.h>
@@ -39,6 +44,7 @@
 
 #define GVA_GCONF_PREFIX                "/apps/" PACKAGE
 #define GVA_GCONF_ALL_COLUMNS_KEY       GVA_GCONF_PREFIX "/all-columns"
+#define GVA_GCONF_AUTO_PLAY_KEY         GVA_GCONF_PREFIX "/auto-play"
 #define GVA_GCONF_AUTO_SAVE_KEY         GVA_GCONF_PREFIX "/auto-save"
 #define GVA_GCONF_COLUMNS_KEY           GVA_GCONF_PREFIX "/columns"
 #define GVA_GCONF_FAVORITES_KEY         GVA_GCONF_PREFIX "/favorites"
diff --git a/src/gva-db.c b/src/gva-db.c
index db17172..a4c941a 100644
--- a/src/gva-db.c
+++ b/src/gva-db.c
@@ -1641,7 +1641,7 @@ db_function_match (sqlite3_context *context,
 static void
 db_trace_cb (gpointer unused, const gchar *message)
 {
-        g_debug ("%s", message);
+        g_log (G_LOG_DOMAIN, GVA_DEBUG_SQL, "%s", message);
 }
 
 /**
diff --git a/src/gva-history.c b/src/gva-history.c
index 8035c03..28240ff 100644
--- a/src/gva-history.c
+++ b/src/gva-history.c
@@ -18,10 +18,21 @@
 
 #include "gva-history.h"
 
+#include <stdlib.h>
+
 #include "gva-error.h"
 
-static GArray *history_file_offset_array = NULL;
-static GHashTable *history_file_offset_table = NULL;
+#define BASE_URI "http://www.arcade-history.com/";
+
+typedef struct _HistoryEntry HistoryEntry;
+
+struct _HistoryEntry {
+        guint id;
+        goffset offset;
+};
+
+static GPtrArray *history_file_array = NULL;
+static GHashTable *history_file_table = NULL;
 
 static GIOChannel *
 history_file_open (GError **error)
@@ -48,30 +59,32 @@ history_file_open (GError **error)
 }
 
 static void
-history_file_mark (const gchar *line, gint64 offset)
+history_process_info (HistoryEntry *entry,
+                      const gchar *line)
 {
         gchar **games;
         guint length, ii;
 
-        g_array_append_val (history_file_offset_array, offset);
-
-        /* Skip "$info=" prefix. */
-        games = g_strsplit (line + 6, ",", -1);
+        games = g_strsplit (line, ",", -1);
         length = g_strv_length (games);
 
         for (ii = 0; ii < length; ii++)
-        {
-                if (*g_strstrip (games[ii]) == '\0')
-                        continue;
-
-                g_hash_table_insert (
-                        history_file_offset_table, g_strdup (games[ii]),
-                        GUINT_TO_POINTER (history_file_offset_array->len - 1));
-        }
+                if (*g_strstrip (games[ii]) != '\0')
+                        g_hash_table_insert (
+                                history_file_table,
+                                g_strdup (games[ii]), entry);
 
         g_strfreev (games);
 }
 
+static void
+history_process_link (HistoryEntry *entry,
+                      const gchar *line)
+{
+        if ((line = strstr (line, "&id=")) != NULL)
+                entry->id = (guint) strtol (line + 4, NULL, 10);
+}
+
 /**
  * gva_history_init:
  * @error: return location for a #GError, or %NULL
@@ -86,26 +99,26 @@ history_file_mark (const gchar *line, gint64 offset)
 gboolean
 gva_history_init (GError **error)
 {
+        HistoryEntry *entry;
         GIOChannel *channel;
         GIOStatus status;
         GString *buffer;
-        gint64 offset = 0;
+        goffset offset = 0;
 
-        g_return_val_if_fail (history_file_offset_array == NULL, FALSE);
-        g_return_val_if_fail (history_file_offset_table == NULL, FALSE);
-
-        channel = history_file_open (error);
-        if (channel == NULL)
-                return FALSE;
+        g_return_val_if_fail (history_file_array == NULL, FALSE);
+        g_return_val_if_fail (history_file_table == NULL, FALSE);
 
-        history_file_offset_array =
-                g_array_new (FALSE, FALSE, sizeof (gint64));
+        history_file_array = g_ptr_array_new ();
 
-        history_file_offset_table = g_hash_table_new_full (
+        history_file_table = g_hash_table_new_full (
                 g_str_hash, g_str_equal,
                 (GDestroyNotify) g_free,
                 (GDestroyNotify) NULL);
 
+        channel = history_file_open (error);
+        if (channel == NULL)
+                return FALSE;
+
         buffer = g_string_sized_new (1024);
 
         while (TRUE)
@@ -121,7 +134,19 @@ gva_history_init (GError **error)
                 offset += buffer->len;
 
                 if (g_str_has_prefix (buffer->str, "$info="))
-                        history_file_mark (buffer->str, offset);
+                {
+                        entry = g_slice_new0 (HistoryEntry);
+                        g_ptr_array_add (history_file_array, entry);
+                        history_process_info (entry, buffer->str + 6);
+                }
+                else if (g_str_has_prefix (buffer->str, "$<a"))
+                {
+                        history_process_link (entry, buffer->str + 1);
+                }
+                else if (g_str_has_prefix (buffer->str, "$bio"))
+                {
+                        entry->offset = offset;
+                }
         }
 
         g_string_free (buffer, TRUE);
@@ -144,18 +169,16 @@ gchar *
 gva_history_lookup (const gchar *game,
                     GError **error)
 {
+        HistoryEntry *entry;
         GIOChannel *channel;
         GIOStatus status;
         GString *buffer;
         GString *history;
         gboolean free_history;
-        gpointer key, value;
-        gint64 offset;
-        guint index;
 
         g_return_val_if_fail (game != NULL, NULL);
-        g_return_val_if_fail (history_file_offset_array != NULL, NULL);
-        g_return_val_if_fail (history_file_offset_table != NULL, NULL);
+        g_return_val_if_fail (history_file_array != NULL, NULL);
+        g_return_val_if_fail (history_file_table != NULL, NULL);
 
         channel = history_file_open (error);
         if (channel == NULL)
@@ -165,18 +188,14 @@ gva_history_lookup (const gchar *game,
         history = g_string_sized_new (8096);
         free_history = TRUE;  /* assume failure */
 
-        if (!g_hash_table_lookup_extended (
-                history_file_offset_table, game, &key, &value))
+        entry = g_hash_table_lookup (history_file_table, game);
+        if (entry == NULL || entry->offset == 0)
                 goto exit;
 
-        index = GPOINTER_TO_UINT (value);
-        g_assert (index < history_file_offset_array->len);
-        offset = g_array_index (history_file_offset_array, gint64, index);
-
         status = G_IO_STATUS_AGAIN;
         while (status == G_IO_STATUS_AGAIN)
                 status = g_io_channel_seek_position (
-                        channel, offset, G_SEEK_SET, error);
+                        channel, entry->offset, G_SEEK_SET, error);
         if (status == G_IO_STATUS_ERROR)
                 goto exit;
 
@@ -230,3 +249,24 @@ exit:
 
         return g_string_free (history, free_history);
 }
+
+/**
+ * gva_history_lookup_id:
+ * @game: the name of a game
+ *
+ * Returns the numeric ID for @game at http://www.arcade-history.com/.
+ * This is used to help locate game-specific resources on the website.
+ *
+ * Returns: ID for @game, or zero if unknown
+ **/
+guint
+gva_history_lookup_id (const gchar *game)
+{
+        HistoryEntry *entry;
+
+        g_return_val_if_fail (game != NULL, 0);
+
+        entry = g_hash_table_lookup (history_file_table, game);
+
+        return (entry != NULL) ? entry->id : 0;
+}
diff --git a/src/gva-history.h b/src/gva-history.h
index d7a8be4..1dd3596 100644
--- a/src/gva-history.h
+++ b/src/gva-history.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
 gboolean        gva_history_init                (GError **error);
 gchar *         gva_history_lookup              (const gchar *game,
                                                  GError **error);
+guint           gva_history_lookup_id           (const gchar *game);
 
 G_END_DECLS
 
diff --git a/src/gva-input-file.c b/src/gva-input-file.c
index e8c14b6..cd63cc7 100644
--- a/src/gva-input-file.c
+++ b/src/gva-input-file.c
@@ -130,12 +130,12 @@ input_file_dump_header (GvaInputFile *input_file)
 
         inpname = g_strdelimit (g_path_get_basename (filename), ".", '\0');
 
-        g_debug ("Input file: %s", inpname);
-        g_debug ("Game: %s", game);
-        g_debug ("%s", format);
-        g_debug ("Created %s", ctime (&timestamp));
-        g_debug ("Recorded using %s", origin);
-        g_debug ("--");
+        g_log (G_LOG_DOMAIN, GVA_DEBUG_INP, "Input file: %s", inpname);
+        g_log (G_LOG_DOMAIN, GVA_DEBUG_INP, "Game: %s", game);
+        g_log (G_LOG_DOMAIN, GVA_DEBUG_INP, "%s", format);
+        g_log (G_LOG_DOMAIN, GVA_DEBUG_INP, "Created %s", ctime (&timestamp));
+        g_log (G_LOG_DOMAIN, GVA_DEBUG_INP, "Recorded using %s", origin);
+        g_log (G_LOG_DOMAIN, GVA_DEBUG_INP, "--");
 
         g_free (inpname);
 }
diff --git a/src/gva-mame-process.c b/src/gva-mame-process.c
index bf938e0..a250fa0 100644
--- a/src/gva-mame-process.c
+++ b/src/gva-mame-process.c
@@ -82,14 +82,12 @@ mame_process_stderr_read_line (GvaProcess *process)
 static void
 mame_process_exited (GvaProcess *process, gint status)
 {
-        if (WIFEXITED (status) && (gva_get_debug_flags () & GVA_DEBUG_MAME))
-        {
-                GPid pid;
-
-                status = WEXITSTATUS (status);
-                pid = gva_process_get_pid (process);
-                g_debug ("Process %d exited with status %d", pid, status);
-        }
+        if (WIFEXITED (status))
+                g_log (
+                        G_LOG_DOMAIN, GVA_DEBUG_MAME,
+                        "Process %d exited with status %d",
+                        gva_process_get_pid (process),
+                        WEXITSTATUS (status));
 }
 
 static void
@@ -201,10 +199,10 @@ gva_mame_process_spawn (const gchar *arguments,
                 command_line, &child_pid, &standard_input,
                 &standard_output, &standard_error, error);
 
-        if (gva_get_debug_flags () & GVA_DEBUG_MAME)
-                g_debug (
-                        "Spawned \"%s\" as process %d",
-                        command_line, (gint) child_pid);
+        g_log (
+                G_LOG_DOMAIN, GVA_DEBUG_MAME,
+                "Spawned \"%s\" as process %d",
+                command_line, (gint) child_pid);
 
         g_free (command_line);
 
diff --git a/src/gva-music-button.c b/src/gva-music-button.c
new file mode 100644
index 0000000..f4671e1
--- /dev/null
+++ b/src/gva-music-button.c
@@ -0,0 +1,646 @@
+/* Copyright 2007-2010 Matthew Barnes
+ *
+ * This file is part of GNOME Video Arcade.
+ *
+ * GNOME Video Arcade is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * GNOME Video Arcade is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gva-music-button.h"
+
+#include "gva-history.h"
+#include "gva-util.h"
+
+#define MUSIC_URI       "http://www.arcade-history.com/mp3/";
+
+#define GVA_MUSIC_BUTTON_GET_PRIVATE(obj) \
+        (G_TYPE_INSTANCE_GET_PRIVATE \
+        ((obj), GVA_TYPE_MUSIC_BUTTON, GvaMusicButtonPrivate))
+
+struct _GvaMusicButtonPrivate
+{
+#ifdef HAVE_GSTREAMER
+        GstElement *element;
+#endif
+
+        const gchar *game;
+
+        gchar *status;
+        guint status_idle_id;
+
+        guint seeking : 1;
+};
+
+enum {
+        PROP_0,
+        PROP_GAME,
+        PROP_STATUS
+};
+
+enum {
+        PAUSE,
+        PLAY,
+        LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static gulong signals[LAST_SIGNAL];
+
+#ifdef HAVE_GSTREAMER
+
+static gboolean
+music_button_notify_status_cb (GvaMusicButton *music_button)
+{
+        music_button->priv->status_idle_id = 0;
+        g_object_notify (G_OBJECT (music_button), "status");
+
+        return FALSE;
+}
+
+static void
+music_button_set_status (GvaMusicButton *music_button,
+                         const gchar *status)
+{
+        g_free (music_button->priv->status);
+        music_button->priv->status = g_strdup (status);
+
+        /* Rate-limit status notifications. */
+        if (music_button->priv->status_idle_id == 0)
+                music_button->priv->status_idle_id = g_idle_add (
+                        (GSourceFunc) music_button_notify_status_cb,
+                        music_button);
+}
+
+static void
+music_button_handle_eos (GvaMusicButton *music_button,
+                         GstMessage *message)
+{
+        GstElement *element;
+        GstSeekFlags flags;
+        GstFormat format;
+
+        element = music_button->priv->element;
+
+        /* Repeat music clip. */
+        format = GST_FORMAT_TIME;
+        flags = GST_SEEK_FLAG_FLUSH;
+        gst_element_seek_simple (element, format, flags, 0);
+
+        music_button->priv->seeking = TRUE;
+}
+
+static void
+music_button_handle_error (GvaMusicButton *music_button,
+                           GstMessage *message)
+{
+        GstElement *element;
+        const gchar *status = NULL;
+        GError *error = NULL;
+
+        element = music_button->priv->element;
+        gst_element_set_state (element, GST_STATE_NULL);
+
+        gst_message_parse_error (message, &error, NULL);
+
+        if (error->domain == GST_RESOURCE_ERROR)
+                status = _("No music available");
+
+        if (status == NULL)
+                status = _("Unable to play music");
+
+        music_button_set_status (music_button, status);
+        gtk_widget_set_sensitive (GTK_WIDGET (music_button), FALSE);
+
+        g_log (
+                G_LOG_DOMAIN, GVA_DEBUG_GST,
+                "%s (code %d):",
+                g_quark_to_string (error->domain),
+                error->code);
+
+        g_log (
+                G_LOG_DOMAIN, GVA_DEBUG_GST,
+                "%s", error->message);
+
+        g_error_free (error);
+}
+
+static void
+music_button_handle_buffering (GvaMusicButton *music_button,
+                               GstMessage *message)
+{
+        gchar *status;
+        gint percent;
+
+        gst_message_parse_buffering (message, &percent);
+
+        status = g_strdup_printf (_("Buffering %d%%..."), percent);
+        music_button_set_status (music_button, status);
+        g_free (status);
+}
+
+static void
+music_button_handle_state_changed (GvaMusicButton *music_button,
+                                   GstMessage *message)
+{
+        GstElement *element;
+        GtkWidget *image;
+        GtkIconSize icon_size;
+        GstState old_state;
+        GstState new_state;
+        GstState pending;
+        gchar *stock_id;
+
+        element = music_button->priv->element;
+
+        image = gtk_button_get_image (GTK_BUTTON (music_button));
+        gtk_image_get_stock (GTK_IMAGE (image), &stock_id, &icon_size);
+
+        gst_message_parse_state_changed (
+                message, &old_state, &new_state, &pending);
+
+        switch (new_state)
+        {
+                case GST_STATE_NULL:
+                        stock_id = GTK_STOCK_MEDIA_PLAY;
+                        break;
+
+                case GST_STATE_READY:
+                        stock_id = GTK_STOCK_MEDIA_PLAY;
+                        break;
+
+                case GST_STATE_PAUSED:
+                        if (music_button->priv->seeking)
+                                stock_id = GTK_STOCK_MEDIA_PAUSE;
+                        else
+                                stock_id = GTK_STOCK_MEDIA_PLAY;
+                        break;
+
+                case GST_STATE_PLAYING:
+                        stock_id = GTK_STOCK_MEDIA_PAUSE;
+                        music_button->priv->seeking = FALSE;
+                        break;
+
+                default:
+                        break;
+        }
+
+        music_button_set_status (music_button, NULL);
+        gtk_image_set_from_stock (GTK_IMAGE (image), stock_id, icon_size);
+
+        g_log (
+                G_LOG_DOMAIN, GVA_DEBUG_GST,
+                "%s -> %s -> %s",
+                gst_element_state_get_name (old_state),
+                gst_element_state_get_name (new_state),
+                gst_element_state_get_name (pending));
+}
+
+static gboolean
+music_button_bus_cb (GstBus *bus,
+                     GstMessage *message,
+                     GvaMusicButton *music_button)
+{
+        g_log (
+                G_LOG_DOMAIN, GVA_DEBUG_GST,
+                "%s", GST_MESSAGE_TYPE_NAME (message));
+
+        switch (GST_MESSAGE_TYPE (message))
+        {
+                case GST_MESSAGE_EOS:
+                        music_button_handle_eos (
+                                music_button, message);
+                        break;
+
+                case GST_MESSAGE_ERROR:
+                        music_button_handle_error (
+                                music_button, message);
+                        break;
+
+                case GST_MESSAGE_BUFFERING:
+                        music_button_handle_buffering (
+                                music_button, message);
+                        break;
+
+                case GST_MESSAGE_STATE_CHANGED:
+                        music_button_handle_state_changed (
+                                music_button, message);
+                        break;
+
+                default:
+                        break;
+        }
+
+        return TRUE;
+}
+
+static void
+music_button_setup_element (GvaMusicButton *music_button)
+{
+        GstElement *element;
+        const gchar *game;
+        gchar *uri;
+        guint id;
+
+        element = music_button->priv->element;
+
+        game = gva_music_button_get_game (music_button);
+        id = (game != NULL) ? gva_history_lookup_id (game) : 0;
+
+        gst_element_set_state (element, GST_STATE_NULL);
+
+        uri = (id > 0) ? g_strdup_printf (MUSIC_URI "%u.mp3", id) : NULL;
+        g_object_set (element, "uri", uri, NULL);
+        g_free (uri);
+
+        music_button_set_status (music_button, _("Connecting..."));
+        gtk_widget_set_sensitive (GTK_WIDGET (music_button), TRUE);
+
+        gst_element_set_state (element, GST_STATE_PAUSED);
+}
+
+#endif /* HAVE_GSTREAMER */
+
+static void
+music_button_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+        switch (property_id)
+        {
+                case PROP_GAME:
+                        gva_music_button_set_game (
+                                GVA_MUSIC_BUTTON (object),
+                                g_value_get_string (value));
+                        return;
+        }
+
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+music_button_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+        switch (property_id)
+        {
+                case PROP_GAME:
+                        g_value_set_string (
+                                value, gva_music_button_get_game (
+                                GVA_MUSIC_BUTTON (object)));
+                        return;
+
+                case PROP_STATUS:
+                        g_value_set_string (
+                                value, gva_music_button_get_status (
+                                GVA_MUSIC_BUTTON (object)));
+                        return;
+        }
+
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+music_button_dispose (GObject *object)
+{
+        GvaMusicButtonPrivate *priv;
+
+        priv = GVA_MUSIC_BUTTON_GET_PRIVATE (object);
+
+#ifdef HAVE_GSTREAMER
+        if (priv->element != NULL)
+        {
+                gst_element_set_state (priv->element, GST_STATE_NULL);
+                g_object_unref (priv->element);
+                priv->element = NULL;
+        }
+#endif
+
+        /* Chain up to parent's dispose() method. */
+        G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+music_button_finalize (GObject *object)
+{
+        GvaMusicButtonPrivate *priv;
+
+        priv = GVA_MUSIC_BUTTON_GET_PRIVATE (object);
+
+        if (priv->status_idle_id > 0)
+                g_source_remove (priv->status_idle_id);
+
+        g_free (priv->status);
+
+        /* Chain up to parent's finalize() method. */
+        G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+music_button_constructed (GObject *object)
+{
+        gtk_button_set_image (
+                GTK_BUTTON (object),
+                gtk_image_new_from_stock (
+                GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON));
+}
+
+static void
+music_button_clicked (GtkButton *button)
+{
+#ifdef HAVE_GSTREAMER
+        GvaMusicButton *music_button;
+        GstElement *element;
+        GstState state;
+
+        music_button = GVA_MUSIC_BUTTON (button);
+        element = music_button->priv->element;
+
+        gst_element_get_state (element, &state, NULL, 0);
+
+        switch (state)
+        {
+                case GST_STATE_NULL:
+                case GST_STATE_READY:
+                case GST_STATE_PAUSED:
+                        gva_music_button_play (music_button);
+                        break;
+
+                case GST_STATE_PLAYING:
+                        gva_music_button_pause (music_button);
+                        break;
+
+                default:
+                        break;
+        }
+#endif
+}
+
+static void
+music_button_pause (GvaMusicButton *music_button)
+{
+#ifdef HAVE_GSTREAMER
+        GstElement *element;
+
+        element = music_button->priv->element;
+
+        gst_element_set_state (element, GST_STATE_PAUSED);
+#endif
+}
+
+static void
+music_button_play (GvaMusicButton *music_button)
+{
+#ifdef HAVE_GSTREAMER
+        GstElement *element;
+
+        element = music_button->priv->element;
+
+        gst_element_set_state (element, GST_STATE_PLAYING);
+#endif
+}
+
+static void
+music_button_class_init (GvaMusicButtonClass *class)
+{
+        GObjectClass *object_class;
+        GtkButtonClass *button_class;
+
+        parent_class = g_type_class_peek_parent (class);
+        g_type_class_add_private (class, sizeof (GvaMusicButtonPrivate));
+
+        object_class = G_OBJECT_CLASS (class);
+        object_class->set_property = music_button_set_property;
+        object_class->get_property = music_button_get_property;
+        object_class->dispose = music_button_dispose;
+        object_class->finalize = music_button_finalize;
+        object_class->constructed = music_button_constructed;
+
+        button_class = GTK_BUTTON_CLASS (class);
+        button_class->clicked = music_button_clicked;
+
+        class->pause = music_button_pause;
+        class->play = music_button_play;
+
+        /**
+         * GvaMusicButton:game
+         *
+         * The game for which to play a music clip.
+         **/
+        g_object_class_install_property (
+                object_class,
+                PROP_GAME,
+                g_param_spec_string (
+                        "game",
+                        NULL,
+                        NULL,
+                        NULL,
+                        G_PARAM_READWRITE));
+
+        /**
+         * GvaMusicButton:status
+         *
+         * Status message about the music clip.
+         **/
+        g_object_class_install_property (
+                object_class,
+                PROP_STATUS,
+                g_param_spec_string (
+                        "status",
+                        NULL,
+                        NULL,
+                        NULL,
+                        G_PARAM_READABLE));
+
+        /**
+         * GvaMusicButton::pause
+         * @music_button: the #GvaMusicButton that received the signal
+         *
+         * The ::pause signal is emitted when the user pauses a music clip.
+         **/
+        signals[PAUSE] = g_signal_new (
+                "pause",
+                G_TYPE_FROM_CLASS (class),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GvaMusicButtonClass, pause),
+                NULL, NULL,
+                g_cclosure_marshal_VOID__VOID,
+                G_TYPE_NONE, 0);
+
+        /**
+         * GvaMusicButton::play
+         * @music_button: the #GvaMusicButton
+         *
+         * The ::play signal is emitted when the user plays a music clip.
+         **/
+        signals[PLAY] = g_signal_new (
+                "play",
+                G_TYPE_FROM_CLASS (class),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GvaMusicButtonClass, play),
+                NULL, NULL,
+                g_cclosure_marshal_VOID__VOID,
+                G_TYPE_NONE, 0);
+}
+
+static void
+music_button_init (GvaMusicButton *music_button)
+{
+#ifdef HAVE_GSTREAMER
+        GstElement *element;
+#endif
+
+        music_button->priv = GVA_MUSIC_BUTTON_GET_PRIVATE (music_button);
+
+#ifdef HAVE_GSTREAMER
+        element = gst_element_factory_make ("playbin", "gva-music-player");
+        music_button->priv->element = element;
+
+        gst_bus_add_watch (
+                gst_element_get_bus (element),
+                (GstBusFunc) music_button_bus_cb, music_button);
+#endif
+}
+
+GType
+gva_music_button_get_type (void)
+{
+        static GType type = 0;
+
+        if (G_UNLIKELY (type == 0))
+        {
+                const GTypeInfo type_info =
+                {
+                        sizeof (GvaMusicButtonClass),
+                        (GBaseInitFunc) NULL,
+                        (GBaseFinalizeFunc) NULL,
+                        (GClassInitFunc) music_button_class_init,
+                        (GClassFinalizeFunc) NULL,
+                        NULL,  /* class_data */
+                        sizeof (GvaMusicButton),
+                        0,     /* n_preallocs */
+                        (GInstanceInitFunc) music_button_init,
+                        NULL   /* value_table */
+                };
+
+                type = g_type_register_static (
+                        GTK_TYPE_BUTTON, "GvaMusicButton", &type_info, 0);
+        }
+
+        return type;
+}
+
+/**
+ * gva_music_button_new:
+ *
+ * Creates a new #GvaMusicButton.
+ *
+ * Returns: a new #GvaMusicButton
+ **/
+GtkWidget *
+gva_music_button_new (void)
+{
+        return g_object_new (GVA_TYPE_MUSIC_BUTTON, NULL);
+}
+
+/**
+ * gva_music_button_play:
+ * @music_button: a #GvaMusicButton
+ *
+ * Plays a music clip from the game specified by the GvaMusicButton:game
+ * property.  The clip will repeat indefinitely until paused or a different
+ * game is chosen.
+ **/
+void
+gva_music_button_play (GvaMusicButton *music_button)
+{
+        g_return_if_fail (GVA_IS_MUSIC_BUTTON (music_button));
+
+        g_signal_emit (music_button, signals[PLAY], 0);
+}
+
+/**
+ * gva_music_button_pause:
+ * @music_button: a #GvaMusicButton
+ *
+ * Pauses a music clip from the game specified by the GvaMusicButton:game
+ * property.
+ **/
+void
+gva_music_button_pause (GvaMusicButton *music_button)
+{
+        g_return_if_fail (GVA_IS_MUSIC_BUTTON (music_button));
+
+        g_signal_emit (music_button, signals[PAUSE], 0);
+}
+
+/**
+ * gva_music_button_get_game:
+ * @music_button: a #GvaMusicButton
+ *
+ * Returns the name of the game for which to play a music clip.
+ *
+ * Returns: the game for which to play a music clip
+ **/
+const gchar *
+gva_music_button_get_game (GvaMusicButton *music_button)
+{
+        g_return_val_if_fail (GVA_IS_MUSIC_BUTTON (music_button), NULL);
+
+        return music_button->priv->game;
+}
+
+/**
+ * gva_music_button_set_game:
+ * @music_button: a #GvaMusicButton
+ * @game: the name of a game
+ *
+ * Sets the name of the game for which to play a music clip.  Use
+ * gva_music_button_play() to play the clip.
+ **/
+void
+gva_music_button_set_game (GvaMusicButton *music_button,
+                           const gchar *game)
+{
+        g_return_if_fail (GVA_IS_MUSIC_BUTTON (music_button));
+
+        if (game != NULL)
+                game = g_intern_string (game);
+
+        music_button->priv->game = game;
+
+#ifdef HAVE_GSTREAMER
+        music_button_setup_element (music_button);
+#endif
+
+        g_object_notify (G_OBJECT (music_button), "game");
+}
+
+/**
+ * gva_music_button_get_status:
+ * @music_button: a #GvaMusicButton
+ *
+ * Returns a status message about the music clip, such as buffering
+ * progress or whether a music clip is available for the selected game.
+ *
+ * Returns: a status message about the music clip
+ **/
+const gchar *
+gva_music_button_get_status (GvaMusicButton *music_button)
+{
+        g_return_val_if_fail (GVA_IS_MUSIC_BUTTON (music_button), NULL);
+
+        return music_button->priv->status;
+}
diff --git a/src/gva-music-button.h b/src/gva-music-button.h
new file mode 100644
index 0000000..e887fc1
--- /dev/null
+++ b/src/gva-music-button.h
@@ -0,0 +1,91 @@
+/* Copyright 2007-2010 Matthew Barnes
+ *
+ * This file is part of GNOME Video Arcade.
+ *
+ * GNOME Video Arcade is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * GNOME Video Arcade is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: gva-music-button
+ * @short_description: Button for streaming music
+ *
+ * A #GvaMusicButton toggles between playing and pausing in-game music
+ * clips streamed from http://www.arcade-history.com/.
+ *
+ * This requires arcade history information from a 'history.dat' file.
+ **/
+
+#ifndef GVA_MUSIC_BUTTON_H
+#define GVA_MUSIC_BUTTON_H
+
+#include "gva-common.h"
+
+/* Standard GObject macros */
+#define GVA_TYPE_MUSIC_BUTTON \
+        (gva_music_button_get_type ())
+#define GVA_MUSIC_BUTTON(obj) \
+        (G_TYPE_CHECK_INSTANCE_CAST \
+        ((obj), GVA_TYPE_MUSIC_BUTTON, GvaMusicButton))
+#define GVA_MUSIC_BUTTON_CLASS(cls) \
+        (G_TYPE_CHECK_CLASS_CAST \
+        ((cls), GVA_TYPE_MUSIC_BUTTON, GvaMusicButtonClass))
+#define GVA_IS_MUSIC_BUTTON(obj) \
+        (G_TYPE_CHECK_INSTANCE_TYPE \
+        ((obj), GVA_TYPE_MUSIC_BUTTON))
+#define GVA_IS_MUSIC_BUTTON_CLASS(cls) \
+        (G_TYPE_CHECK_CLASS_TYPE \
+        ((cls), GVA_TYPE_MUSIC_BUTTON))
+#define GVA_MUSIC_BUTTON_GET_CLASS(obj) \
+        (G_TYPE_INSTANCE_GET_CLASS \
+        ((obj), GVA_TYPE_MUSIC_BUTTON, GvaMusicButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GvaMusicButton GvaMusicButton;
+typedef struct _GvaMusicButtonClass GvaMusicButtonClass;
+typedef struct _GvaMusicButtonPrivate GvaMusicButtonPrivate;
+
+/**
+ * GvaMusicButton:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _GvaMusicButton
+{
+        GtkButton parent;
+        GvaMusicButtonPrivate *priv;
+};
+
+struct _GvaMusicButtonClass
+{
+        GtkButtonClass parent_class;
+
+        /* Signals */
+        void            (*play)                 (GvaMusicButton *music_button);
+        void            (*pause)                (GvaMusicButton *music_button);
+};
+
+GType           gva_music_button_get_type       (void);
+GtkWidget *     gva_music_button_new            (void);
+void            gva_music_button_play           (GvaMusicButton *music_button);
+void            gva_music_button_pause          (GvaMusicButton *music_button);
+const gchar *   gva_music_button_get_game       (GvaMusicButton *music_button);
+void            gva_music_button_set_game       (GvaMusicButton *music_button,
+                                                 const gchar *game);
+const gchar *   gva_music_button_get_status     (GvaMusicButton *music_button);
+
+G_END_DECLS
+
+#endif /* GVA_MUSIC_BUTTON_H */
diff --git a/src/gva-preferences.c b/src/gva-preferences.c
index a39bc80..c3c4408 100644
--- a/src/gva-preferences.c
+++ b/src/gva-preferences.c
@@ -33,6 +33,19 @@
 void
 gva_preferences_init (void)
 {
+        /* Auto Play */
+
+        /* This actually appears in the Properties window,
+         * but it's still a preference so we manage it here. */
+
+        gtk_action_connect_proxy (
+                GVA_ACTION_AUTO_PLAY,
+                GVA_WIDGET_PROPERTIES_MUSIC_AUTO_PLAY);
+
+        gconf_bridge_bind_property (
+                gconf_bridge_get (), GVA_GCONF_AUTO_PLAY_KEY,
+                G_OBJECT (GVA_ACTION_AUTO_PLAY), "active");
+
         /* Auto Save */
 
         gtk_action_connect_proxy (
@@ -74,6 +87,48 @@ gva_preferences_init (void)
 }
 
 /**
+ * gva_preferences_get_auto_play:
+ *
+ * Returns the user's preference for whether to automatically
+ * play a music clip of the selected game when the user opens
+ * the Properties window or when the user selects a different
+ * game while the Properties window is visible.
+ *
+ * Returns: %TRUE to automatically play music clips
+ **/
+gboolean
+gva_preferences_get_auto_play (void)
+{
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_AUTO_PLAY);
+
+        return gtk_toggle_action_get_active (toggle_action);
+}
+
+/**
+ * gva_preferences_set_auto_play:
+ * @auto_play: the user's preference
+ *
+ * Accepts the user's preference for whether to automatically
+ * play a music clip of the selected game when the user opens
+ * the Properties window or when the user selects a different
+ * game while the Properties window is visible.
+ *
+ * The preference is stored in GConf key
+ * <filename>/apps/gnome-video-arcade/auto-play</filename>.
+ **/
+void
+gva_preferences_set_auto_play (gboolean auto_play)
+{
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_AUTO_PLAY);
+
+        gtk_toggle_action_set_active (toggle_action, auto_play);
+}
+
+/**
  * gva_preferences_get_auto_save:
  *
  * Returns the user's preference for whether to restore the emulated
@@ -84,8 +139,11 @@ gva_preferences_init (void)
 gboolean
 gva_preferences_get_auto_save (void)
 {
-        return gtk_toggle_action_get_active (
-                GTK_TOGGLE_ACTION (GVA_ACTION_AUTO_SAVE));
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_AUTO_SAVE);
+
+        return gtk_toggle_action_get_active (toggle_action);
 }
 
 /**
@@ -101,8 +159,11 @@ gva_preferences_get_auto_save (void)
 void
 gva_preferences_set_auto_save (gboolean auto_save)
 {
-        gtk_toggle_action_set_active (
-                GTK_TOGGLE_ACTION (GVA_ACTION_AUTO_SAVE), auto_save);
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_AUTO_SAVE);
+
+        gtk_toggle_action_set_active (toggle_action, auto_save);
 }
 
 /**
@@ -116,8 +177,11 @@ gva_preferences_set_auto_save (gboolean auto_save)
 gboolean
 gva_preferences_get_full_screen (void)
 {
-        return gtk_toggle_action_get_active (
-                GTK_TOGGLE_ACTION (GVA_ACTION_FULL_SCREEN));
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_FULL_SCREEN);
+
+        return gtk_toggle_action_get_active (toggle_action);
 }
 
 /**
@@ -133,8 +197,11 @@ gva_preferences_get_full_screen (void)
 void
 gva_preferences_set_full_screen (gboolean full_screen)
 {
-        gtk_toggle_action_set_active (
-                GTK_TOGGLE_ACTION (GVA_ACTION_FULL_SCREEN), full_screen);
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_FULL_SCREEN);
+
+        gtk_toggle_action_set_active (toggle_action, full_screen);
 }
 
 /**
@@ -148,8 +215,11 @@ gva_preferences_set_full_screen (gboolean full_screen)
 gboolean
 gva_preferences_get_show_clones (void)
 {
-        return gtk_toggle_action_get_active (
-                GTK_TOGGLE_ACTION (GVA_ACTION_SHOW_CLONES));
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_SHOW_CLONES);
+
+        return gtk_toggle_action_get_active (toggle_action);
 }
 
 /**
@@ -165,8 +235,11 @@ gva_preferences_get_show_clones (void)
 void
 gva_preferences_set_show_clones (gboolean show_clones)
 {
-        gtk_toggle_action_set_active (
-                GTK_TOGGLE_ACTION (GVA_ACTION_SHOW_CLONES), show_clones);
+        GtkToggleAction *toggle_action;
+
+        toggle_action = GTK_TOGGLE_ACTION (GVA_ACTION_SHOW_CLONES);
+
+        gtk_toggle_action_set_active (toggle_action, show_clones);
 }
 
 /**
diff --git a/src/gva-preferences.h b/src/gva-preferences.h
index 2816327..b29edcb 100644
--- a/src/gva-preferences.h
+++ b/src/gva-preferences.h
@@ -31,6 +31,8 @@
 G_BEGIN_DECLS
 
 void           gva_preferences_init             (void);
+gboolean       gva_preferences_get_auto_play    (void);
+void           gva_preferences_set_auto_play    (gboolean auto_play);
 gboolean       gva_preferences_get_auto_save    (void);
 void           gva_preferences_set_auto_save    (gboolean auto_save);
 gboolean       gva_preferences_get_full_screen  (void);
diff --git a/src/gva-process.c b/src/gva-process.c
index f0e6c80..12aa042 100644
--- a/src/gva-process.c
+++ b/src/gva-process.c
@@ -258,7 +258,9 @@ process_debug_message (GvaProcess *process,
         pid = gva_process_get_pid (process);
         copy = g_strchomp (g_strdup (line));
 
-        g_debug ("Process %d %s %s", (gint) pid, sep, copy);
+        g_log (
+                G_LOG_DOMAIN, GVA_DEBUG_IO,
+                "Process %d %s %s", (gint) pid, sep, copy);
 
         g_free (copy);
 }
@@ -449,7 +451,7 @@ process_class_init (GvaProcessClass *class)
         class->stderr_read_line = process_stderr_read_line;
 
         /**
-         * GvaProcess:pid:
+         * GvaProcess:pid
          *
          * The ID of the child process.
          **/
@@ -465,7 +467,7 @@ process_class_init (GvaProcessClass *class)
                         G_PARAM_CONSTRUCT_ONLY));
 
         /**
-         * GvaProcess:stdin:
+         * GvaProcess:stdin
          *
          * The file descriptor for the child process' stdin pipe.
          **/
@@ -481,7 +483,7 @@ process_class_init (GvaProcessClass *class)
                         G_PARAM_CONSTRUCT_ONLY));
 
         /**
-         * GvaProcess:stdout:
+         * GvaProcess:stdout
          *
          * The file descriptor for the child process' stdout pipe.
          **/
@@ -497,7 +499,7 @@ process_class_init (GvaProcessClass *class)
                         G_PARAM_CONSTRUCT_ONLY));
 
         /**
-         * GvaProcess:stderr:
+         * GvaProcess:stderr
          *
          * The file descriptor for the child process' stderr pipe.
          **/
@@ -513,7 +515,7 @@ process_class_init (GvaProcessClass *class)
                         G_PARAM_CONSTRUCT_ONLY));
 
         /**
-         * GvaProcess:priority:
+         * GvaProcess:priority
          *
          * Priority of the event sources that watch for incoming data.
          **/
@@ -530,7 +532,7 @@ process_class_init (GvaProcessClass *class)
                         G_PARAM_CONSTRUCT_ONLY));
 
         /**
-         * GvaProcess:progress:
+         * GvaProcess:progress
          *
          * Progress value, the meaning of which is defined by the
          * application.
@@ -747,8 +749,7 @@ gva_process_write_stdin (GvaProcess *process,
         g_return_val_if_fail (GVA_IS_PROCESS (process), FALSE);
         g_return_val_if_fail (data != NULL, FALSE);
 
-        if (gva_get_debug_flags () & GVA_DEBUG_IO)
-                process_debug_message (process, data, "<<<");
+        process_debug_message (process, data, "<<<");
 
         while (status == G_IO_STATUS_AGAIN)
                 status = g_io_channel_write_chars (
@@ -828,8 +829,7 @@ gva_process_stdout_read_line (GvaProcess *process)
         g_return_val_if_fail (class->stdout_read_line != NULL, NULL);
         line = class->stdout_read_line (process);
 
-        if (gva_get_debug_flags () & GVA_DEBUG_IO)
-                process_debug_message (process, line, ">>>");
+        process_debug_message (process, line, ">>>");
 
         return line;
 }
@@ -858,8 +858,7 @@ gva_process_stderr_read_line (GvaProcess *process)
         g_return_val_if_fail (class->stderr_read_line != NULL, NULL);
         line = class->stderr_read_line (process);
 
-        if (gva_get_debug_flags () & GVA_DEBUG_IO)
-                process_debug_message (process, line, "!!!");
+        process_debug_message (process, line, "!!!");
 
         return line;
 }
diff --git a/src/gva-properties.c b/src/gva-properties.c
index 05db8c9..667b340 100644
--- a/src/gva-properties.c
+++ b/src/gva-properties.c
@@ -26,6 +26,8 @@
 #include "gva-game-store.h"
 #include "gva-history.h"
 #include "gva-link-button.h"
+#include "gva-music-button.h"
+#include "gva-preferences.h"
 #include "gva-tree-view.h"
 #include "gva-ui.h"
 #include "gva-util.h"
@@ -473,6 +475,19 @@ properties_update_history (GtkTreeModel *model,
 }
 
 static void
+properties_update_music (const gchar *name)
+{
+        GvaMusicButton *music_button;
+
+        music_button = GVA_MUSIC_BUTTON (GVA_WIDGET_PROPERTIES_MUSIC_BUTTON);
+        gva_music_button_set_game (music_button, name);
+
+        if (GTK_WIDGET_VISIBLE (GVA_WIDGET_PROPERTIES_WINDOW)
+                && gva_preferences_get_auto_play ())
+                gva_music_button_play (music_button);
+}
+
+static void
 properties_update_sound (const gchar *name)
 {
         GtkWidget *label;
@@ -722,6 +737,25 @@ properties_selection_changed_cb (GtkTreeSelection *selection)
                 properties_update_timeout_cb, NULL);
 }
 
+static void
+properties_notify_music_status_cb (GvaMusicButton *music_button)
+{
+        GtkWidget *label;
+        const gchar *status;
+        gchar *markup;
+
+        label = GVA_WIDGET_PROPERTIES_MUSIC_STATUS;
+        status = gva_music_button_get_status (music_button);
+
+        /* Use whitespace to keep the widget height stable. */
+        if (status == NULL)
+                status = " ";
+
+        markup = g_markup_printf_escaped ("<small>%s</small>", status);
+        gtk_label_set_markup (GTK_LABEL (label), markup);
+        g_free (markup);
+}
+
 /**
  * gva_properties_init:
  *
@@ -764,6 +798,10 @@ gva_properties_init (void)
                 gtk_tree_view_get_selection (view), "changed",
                 G_CALLBACK (properties_selection_changed_cb), NULL);
 
+        g_signal_connect (
+                GVA_WIDGET_PROPERTIES_MUSIC_BUTTON, "notify::status",
+                G_CALLBACK (properties_notify_music_status_cb), NULL);
+
         font_name = gva_get_monospace_font_name ();
         desc = pango_font_description_from_string (font_name);
         gtk_widget_modify_font (text_view, desc);
@@ -778,6 +816,10 @@ gva_properties_init (void)
         widget = GVA_WIDGET_PROPERTIES_STATUS_FRAME;
         gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, color);
 
+#ifndef HAVE_GSTREAMER
+        gtk_widget_hide (GVA_WIDGET_PROPERTIES_MUSIC_TABLE);
+#endif
+
 #ifndef HISTORY_FILE
         /* Hide the history page if we have no history file. */
         notebook = GTK_NOTEBOOK (GVA_WIDGET_PROPERTIES_NOTEBOOK);
@@ -829,6 +871,7 @@ gva_properties_show_game (const gchar *game)
                 properties_update_cpu (game);
                 properties_update_header (model, &iter);
                 properties_update_history (model, &iter);
+                properties_update_music (game);
                 properties_update_sound (game);
                 properties_update_status (model, &iter);
                 properties_update_video (game);
@@ -838,19 +881,21 @@ gva_properties_show_game (const gchar *game)
 }
 
 /**
- * gva_properties_close_clicked_cb:
+ * gva_properties_hide_cb:
  * @window: the "Properties" window
  * @button: the "Close" button
  *
- * Handler for #GtkButton::clicked signals to the "Close" button.
+ * Handler for #GtkWidget::show signals to the "Properties" window.
  *
- * Hides @window.
+ * Stops in-game music clip.
  **/
 void
-gva_properties_close_clicked_cb (GtkWindow *window,
-                                 GtkButton *button)
+gva_properties_hide_cb (GtkWindow *window)
 {
-        gtk_widget_hide (GTK_WIDGET (window));
+        GvaMusicButton *music_button;
+
+        music_button = GVA_MUSIC_BUTTON (GVA_WIDGET_PROPERTIES_MUSIC_BUTTON);
+        gva_music_button_pause (music_button);
 }
 
 /**
@@ -859,10 +904,18 @@ gva_properties_close_clicked_cb (GtkWindow *window,
  *
  * Handler for #GtkWidget::show signals to the "Properties" window.
  *
- * Resets all scrolled windows to the top.
+ * Resets all scrolled windows to the top, and starts in-game music clip
+ * if the "auto-play" preference is enabled.
  **/
 void
 gva_properties_show_cb (GtkWindow *window)
 {
+        GvaMusicButton *music_button;
+
+        music_button = GVA_MUSIC_BUTTON (GVA_WIDGET_PROPERTIES_MUSIC_BUTTON);
+
+        if (gva_preferences_get_auto_play ())
+                gva_music_button_play (music_button);
+
         properties_scroll_to_top ();
 }
diff --git a/src/gva-properties.h b/src/gva-properties.h
index f9161e2..48db872 100644
--- a/src/gva-properties.h
+++ b/src/gva-properties.h
@@ -35,8 +35,7 @@ void            gva_properties_show_game        (const gchar *game);
 
 /* Signal Handlers */
 
-void            gva_properties_close_clicked_cb (GtkWindow *window,
-                                                 GtkButton *button);
+void            gva_properties_hide_cb          (GtkWindow *window);
 void            gva_properties_show_cb          (GtkWindow *window);
 
 G_END_DECLS
diff --git a/src/gva-ui.c b/src/gva-ui.c
index 74adf6f..0d79d6f 100644
--- a/src/gva-ui.c
+++ b/src/gva-ui.c
@@ -26,6 +26,7 @@
 #include "gva-game-store.h"
 #include "gva-main.h"
 #include "gva-mame.h"
+#include "gva-music-button.h"
 #include "gva-mute-button.h"
 #include "gva-play-back.h"
 #include "gva-preferences.h"
@@ -171,6 +172,14 @@ action_add_column_cb (GtkAction *action,
 }
 
 /**
+ * GVA_ACTION_AUTO_PLAY:
+ *
+ * This toggle action tracks the user's preference for whether to
+ * automatically start playing a music clip from the selected game
+ * (if available) when the Properties window is open.
+ **/
+
+/**
  * GVA_ACTION_AUTO_SAVE:
  *
  * This toggle action tracks the user's preference for whether to
@@ -281,6 +290,7 @@ action_next_game_cb (GtkAction *action)
 static void
 action_play_back_cb (GtkAction *action)
 {
+        GtkWidget *widget;
         GvaProcess *process;
         GtkTreeModel *model;
         GtkTreeView *view;
@@ -309,6 +319,9 @@ action_play_back_cb (GtkAction *action)
         inpname = g_strdelimit (g_path_get_basename (inpfile), ".", '\0');
         g_free (inpfile);
 
+        widget = GVA_WIDGET_PROPERTIES_MUSIC_BUTTON;
+        gva_music_button_pause (GVA_MUSIC_BUTTON (widget));
+
         process = gva_mame_playback_game (name, inpname, &error);
         gva_error_handle (&error);
 
@@ -429,6 +442,7 @@ action_quit_cb (GtkAction *action)
 static void
 action_record_cb (GtkAction *action)
 {
+        GtkWidget *widget;
         GvaProcess *process;
         const gchar *name;
         gchar *inpname;
@@ -439,6 +453,9 @@ action_record_cb (GtkAction *action)
 
         inpname = gva_choose_inpname (name);
 
+        widget = GVA_WIDGET_PROPERTIES_MUSIC_BUTTON;
+        gva_music_button_pause (GVA_MUSIC_BUTTON (widget));
+
         process = gva_mame_record_game (name, inpname, &error);
         gva_error_handle (&error);
 
@@ -604,6 +621,7 @@ action_show_play_back_cb (GtkAction *action)
 static void
 action_start_cb (GtkAction *action)
 {
+        GtkWidget *widget;
         GvaProcess *process;
         const gchar *name;
         GError *error = NULL;
@@ -614,6 +632,9 @@ action_start_cb (GtkAction *action)
         if (!gva_preferences_get_auto_save ())
                 gva_mame_delete_save_state (name);
 
+        widget = GVA_WIDGET_PROPERTIES_MUSIC_BUTTON;
+        gva_music_button_pause (GVA_MUSIC_BUTTON (widget));
+
         process = gva_mame_run_game (name, &error);
         gva_error_handle (&error);
 
@@ -828,6 +849,14 @@ static GtkActionEntry entries[] =
 
 static GtkToggleActionEntry toggle_entries[] =
 {
+        { "auto-play",
+          NULL,
+          N_("Play music _automatically"),
+          NULL,
+          N_("Automatically play a music clip from the selected game"),
+          NULL,     /* GConfBridge monitors the state */
+          FALSE },  /* GConf overrides this */
+
         { "auto-save",
           NULL,
           N_("_Restore previous state when starting a game"),
diff --git a/src/gva-ui.h b/src/gva-ui.h
index 986cc26..2789be3 100644
--- a/src/gva-ui.h
+++ b/src/gva-ui.h
@@ -31,6 +31,7 @@
 
 /* Actions */
 #define GVA_ACTION_ABOUT                (gva_ui_get_action ("about"))
+#define GVA_ACTION_AUTO_PLAY            (gva_ui_get_action ("auto-play"))
 #define GVA_ACTION_AUTO_SAVE            (gva_ui_get_action ("auto-save"))
 #define GVA_ACTION_CONTENTS             (gva_ui_get_action ("contents"))
 #define GVA_ACTION_FULL_SCREEN          (gva_ui_get_action ("full-screen"))
@@ -142,6 +143,14 @@
         (gva_ui_get_widget ("properties-imperfect-graphic-label"))
 #define GVA_WIDGET_PROPERTIES_IMPERFECT_SOUND_LABEL \
         (gva_ui_get_widget ("properties-imperfect-sound-label"))
+#define GVA_WIDGET_PROPERTIES_MUSIC_AUTO_PLAY \
+        (gva_ui_get_widget ("properties-music-auto-play"))
+#define GVA_WIDGET_PROPERTIES_MUSIC_BUTTON \
+        (gva_ui_get_widget ("properties-music-button"))
+#define GVA_WIDGET_PROPERTIES_MUSIC_STATUS \
+        (gva_ui_get_widget ("properties-music-status"))
+#define GVA_WIDGET_PROPERTIES_MUSIC_TABLE \
+        (gva_ui_get_widget ("properties-music-table"))
 #define GVA_WIDGET_PROPERTIES_NOTEBOOK \
         (gva_ui_get_widget ("properties-notebook"))
 #define GVA_WIDGET_PROPERTIES_ORIGINAL_LINKS \
diff --git a/src/gva-util.c b/src/gva-util.c
index 08ec21c..b6afb24 100644
--- a/src/gva-util.c
+++ b/src/gva-util.c
@@ -38,6 +38,18 @@ gchar *opt_inspect;
 gboolean opt_version;
 gboolean opt_which_emulator;
 
+static void
+log_handler (const gchar *log_domain,
+             GLogLevelFlags log_level,
+             const gchar *message,
+             const gchar *log_level_id)
+{
+        if ((gva_get_debug_flags () & log_level) != 0)
+                g_print (
+                        "%s-LOG-DEBUG-%s: %s\n",
+                        log_domain, log_level_id, message);
+}
+
 static gboolean
 inpname_exists (const gchar *inppath, const gchar *inpname)
 {
@@ -166,11 +178,28 @@ gva_get_debug_flags (void)
                         { "mame",  GVA_DEBUG_MAME },
                         { "sql",   GVA_DEBUG_SQL },
                         { "io",    GVA_DEBUG_IO },
-                        { "inp",   GVA_DEBUG_INP }
+                        { "inp",   GVA_DEBUG_INP },
+                        { "gst",   GVA_DEBUG_GST }
                 };
 
                 const gchar *env = g_getenv ("GVA_DEBUG");
 
+                g_log_set_handler (
+                        G_LOG_DOMAIN, GVA_DEBUG_MAME,
+                        (GLogFunc) log_handler, "MAME");
+                g_log_set_handler (
+                        G_LOG_DOMAIN, GVA_DEBUG_SQL,
+                        (GLogFunc) log_handler, "SQL");
+                g_log_set_handler (
+                        G_LOG_DOMAIN, GVA_DEBUG_IO,
+                        (GLogFunc) log_handler, "IO");
+                g_log_set_handler (
+                        G_LOG_DOMAIN, GVA_DEBUG_INP,
+                        (GLogFunc) log_handler, "INP");
+                g_log_set_handler (
+                        G_LOG_DOMAIN, GVA_DEBUG_GST,
+                        (GLogFunc) log_handler, "GST");
+
                 flags = g_parse_debug_string (
                         (env != NULL) ? env : "", debug_keys,
                         G_N_ELEMENTS (debug_keys));
diff --git a/src/gva-util.h b/src/gva-util.h
index 3045d4b..1de1c6e 100644
--- a/src/gva-util.h
+++ b/src/gva-util.h
@@ -43,6 +43,8 @@ G_BEGIN_DECLS
  *      Print all communication between GVA and MAME.
  * @GVA_DEBUG_INP:
  *      Print information about input files.
+ * @GVA_DEBUG_GST:
+ *      Print GStreamer activity.
  *
  * These flags indicate which types of debugging messages will be triggered
  * at runtime. Debugging messages can be triggered by setting the GVA_DEBUG
@@ -50,11 +52,12 @@ G_BEGIN_DECLS
  **/
 typedef enum
 {
-        GVA_DEBUG_NONE = 0,
-        GVA_DEBUG_MAME = 1 << 0,
-        GVA_DEBUG_SQL  = 1 << 1,
-        GVA_DEBUG_IO   = 1 << 2,
-        GVA_DEBUG_INP  = 1 << 3
+        GVA_DEBUG_NONE  = 0,
+        GVA_DEBUG_MAME  = 1 << (G_LOG_LEVEL_USER_SHIFT + 0),
+        GVA_DEBUG_SQL   = 1 << (G_LOG_LEVEL_USER_SHIFT + 1),
+        GVA_DEBUG_IO    = 1 << (G_LOG_LEVEL_USER_SHIFT + 2),
+        GVA_DEBUG_INP   = 1 << (G_LOG_LEVEL_USER_SHIFT + 3),
+        GVA_DEBUG_GST   = 1 << (G_LOG_LEVEL_USER_SHIFT + 4)
 } GvaDebugFlags;
 
 gchar *         gva_choose_inpname              (const gchar *game);
diff --git a/src/main.c b/src/main.c
index c1a5f28..f99a014 100644
--- a/src/main.c
+++ b/src/main.c
@@ -233,6 +233,14 @@ main (gint argc, gchar **argv)
                 g_error ("%s", error->message);
 #endif
 
+#ifdef HAVE_GSTREAMER
+        if (!gst_init_check (&argc, &argv, &error))
+                g_error ("%s", error->message);
+#endif
+
+        /* This installs handlers for our custom debug log levels. */
+        gva_get_debug_flags ();
+
         /* Change the working directory to that of the MAME executable.
          * Why?  Because SDLMAME's default configuration uses relative
          * search paths such as "rompath = roms".  The paths are relative



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]