[rhythmbox] new more compact header layout



commit cc6633bb330c3ba8f63d46c0c3e785a2b631c2ba
Author: Jonathan Matthew <jonathan d14n org>
Date:   Wed Mar 28 22:52:18 2012 +1000

    new more compact header layout
    
    Now we have the playback controls, the current track information
    (now including album art), the time display (which can be toggled
    between elapsed and remaining time), the position slider (much
    shorter than before) and the volume control on the same line.
    
    With this layout it makes no sense to hide the toolbar, so that
    option is no longer available.  It also makes no sense to use
    a toolbar style other than 'text below icons', so this option
    has been removed too.
    
    Small display mode is also gone.  It needs to be reimplemented
    as a separate window.
    
    This took me far too long land.

 data/org.gnome.rhythmbox.gschema.xml |   27 +--
 data/ui/general-prefs.ui             |  180 ++++-------
 data/ui/rhythmbox-ui.xml             |    7 +-
 doc/reference/rhythmbox-sections.txt |    2 -
 shell/rb-shell-player.c              |  142 +++++----
 shell/rb-shell-player.h              |    6 +-
 shell/rb-shell-preferences.c         |   46 ---
 shell/rb-shell.c                     |  268 +++------------
 widgets/rb-header.c                  |  599 +++++++++++++++++++++++++---------
 widgets/rb-header.h                  |   11 +-
 10 files changed, 647 insertions(+), 641 deletions(-)
---
diff --git a/data/org.gnome.rhythmbox.gschema.xml b/data/org.gnome.rhythmbox.gschema.xml
index 4d6458c..530fdf1 100644
--- a/data/org.gnome.rhythmbox.gschema.xml
+++ b/data/org.gnome.rhythmbox.gschema.xml
@@ -37,26 +37,6 @@
       <summary>Main window position</summary>
       <description>The position of the window, if not maximized. (-1, -1) indicates no specific position.</description>
     </key>
-    <key name="small-display" type="b">
-      <default>false</default>
-      <summary>Small display mode</summary>
-      <description>If true, the main window is displayed without the source list, browser or playlist.</description>
-    </key>
-    <key name="small-width" type="i">
-      <default>600</default>
-      <summary>Small window width</summary>
-      <description>Main window width when the player is in Small Display mode.</description>
-    </key>
-    <key name="toolbar-visible" type="b">
-      <default>true</default>
-      <summary>Toolbar visibility</summary>
-      <description>If true, the toolbar is visible.</description>
-    </key>
-    <key name="toolbar-style" type="i">
-      <default>0</default>
-      <summary>Toolbar style</summary>
-      <description>0=GNOME default, 1=Text below icons, 2=Text beside icons, 3=Icons only, 4=Text only</description>
-    </key>
     <key name="display-page-tree-visible" type="b">
       <default>true</default>
       <summary>Display page tree visibility</summary>
@@ -88,10 +68,15 @@
       <description>If true, the play queue is displayed as a sidebar. If false, the play queue is displayed as a source in the source list.</description>
     </key>
     <key name="show-song-position-slider" type="b">
-      <default>false</default>
+      <default>true</default>
       <summary>Song position slider visibility</summary>
       <description>If true, the song position slider is shown.</description>
     </key>
+    <key name="show-album-art" type="b">
+      <default>true</default>
+      <summary>Album art display visibility</summary>
+      <description>If true, the album art display is shown.</description>
+    </key>
     <key name="time-display" type="b">
       <default>true</default>
       <summary>Whether to show elapsed or remaining time</summary>
diff --git a/data/ui/general-prefs.ui b/data/ui/general-prefs.ui
index 2d800af..4a1833d 100644
--- a/data/ui/general-prefs.ui
+++ b/data/ui/general-prefs.ui
@@ -1,47 +1,21 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 2.12 -->
-  <!-- interface-naming-policy toplevel-contextual -->
-  <object class="GtkListStore" id="model1">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">Default</col>
-      </row>
-      <row>
-        <col id="0">-</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">Text below icons</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">Text beside icons</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">Icons only</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">Text only</col>
-      </row>
-    </data>
-  </object>
   <object class="GtkVBox" id="general_vbox">
     <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="valign">start</property>
     <property name="border_width">12</property>
-    <property name="orientation">vertical</property>
     <property name="spacing">18</property>
-    <property name="valign">start</property>
     <child>
       <object class="GtkVBox" id="vbox14">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkLabel" id="browser_views_label">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="xalign">0</property>
             <property name="label" translatable="yes">Browser Views</property>
           </object>
@@ -54,9 +28,11 @@
         <child>
           <object class="GtkHBox" id="hbox16">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <child>
               <object class="GtkLabel" id="label23">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -67,9 +43,11 @@
             <child>
               <object class="GtkHBox" id="hbox17">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <child>
                   <object class="GtkLabel" id="label20">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="xpad">8</property>
                   </object>
                   <packing>
@@ -81,7 +59,7 @@
                 <child>
                   <object class="GtkVBox" id="vbox15">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
+                    <property name="can_focus">False</property>
                     <property name="spacing">6</property>
                     <child>
                       <object class="GtkRadioButton" id="library_browser_views_radio">
@@ -89,10 +67,11 @@
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
+                        <property name="use_action_appearance">False</property>
                         <property name="use_underline">True</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <signal name="toggled" handler="rb_shell_preferences_browser_views_activated_cb"/>
+                        <signal name="toggled" handler="rb_shell_preferences_browser_views_activated_cb" swapped="no"/>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -106,10 +85,11 @@
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
+                        <property name="use_action_appearance">False</property>
                         <property name="use_underline">True</property>
                         <property name="draw_indicator">True</property>
                         <property name="group">library_browser_views_radio</property>
-                        <signal name="toggled" handler="rb_shell_preferences_browser_views_activated_cb"/>
+                        <signal name="toggled" handler="rb_shell_preferences_browser_views_activated_cb" swapped="no"/>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -123,10 +103,11 @@
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
+                        <property name="use_action_appearance">False</property>
                         <property name="use_underline">True</property>
                         <property name="draw_indicator">True</property>
                         <property name="group">library_browser_views_radio</property>
-                        <signal name="toggled" handler="rb_shell_preferences_browser_views_activated_cb"/>
+                        <signal name="toggled" handler="rb_shell_preferences_browser_views_activated_cb" swapped="no"/>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -136,38 +117,46 @@
                     </child>
                   </object>
                   <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">1</property>
               </packing>
             </child>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
       </object>
       <packing>
         <property name="expand">False</property>
+        <property name="fill">True</property>
         <property name="position">0</property>
       </packing>
     </child>
     <child>
       <object class="GtkVBox" id="vbox2">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkVBox" id="vbox12">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="can_focus">False</property>
             <property name="spacing">6</property>
             <child>
               <object class="GtkLabel" id="visible_columns_label">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="xalign">0</property>
                 <property name="label" translatable="yes">Visible Columns</property>
               </object>
@@ -180,9 +169,11 @@
             <child>
               <object class="GtkHBox" id="hbox14">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <child>
                   <object class="GtkLabel" id="label1">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
@@ -193,14 +184,16 @@
                 <child>
                   <object class="GtkVBox" id="vbox13">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
+                    <property name="can_focus">False</property>
                     <property name="spacing">6</property>
                     <child>
                       <object class="GtkHBox" id="hbox15">
                         <property name="visible">True</property>
+                        <property name="can_focus">False</property>
                         <child>
                           <object class="GtkLabel" id="label21">
                             <property name="visible">True</property>
+                            <property name="can_focus">False</property>
                             <property name="xpad">8</property>
                           </object>
                           <packing>
@@ -212,6 +205,7 @@
                         <child>
                           <object class="GtkTable" id="table1">
                             <property name="visible">True</property>
+                            <property name="can_focus">False</property>
                             <property name="n_rows">5</property>
                             <property name="n_columns">3</property>
                             <property name="column_spacing">12</property>
@@ -223,6 +217,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -236,6 +231,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -251,6 +247,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -266,6 +263,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -282,6 +280,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -298,6 +297,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -316,6 +316,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -332,6 +333,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -350,6 +352,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -368,6 +371,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -386,6 +390,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -404,6 +409,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -422,6 +428,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -440,6 +447,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="use_underline">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -455,6 +463,8 @@
                             </child>
                           </object>
                           <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
                             <property name="position">1</property>
                           </packing>
                         </child>
@@ -467,11 +477,15 @@
                     </child>
                   </object>
                   <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">1</property>
               </packing>
             </child>
@@ -490,99 +504,17 @@
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="vbox16">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">6</property>
-        <child>
-          <object class="GtkLabel" id="toolbar_style_label">
-            <property name="visible">True</property>
-            <property name="xalign">0</property>
-            <property name="label" translatable="yes">Toolbar Button Labels</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkHBox" id="hbox18">
-            <property name="visible">True</property>
-            <child>
-              <object class="GtkLabel" id="label25">
-                <property name="visible">True</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkHBox" id="hbox19">
-                <property name="visible">True</property>
-                <child>
-                  <object class="GtkLabel" id="label26">
-                    <property name="visible">True</property>
-                    <property name="xpad">8</property>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkComboBox" id="toolbar_style_menu">
-                    <property name="visible">True</property>
-                    <property name="model">model1</property>
-                    <child>
-                      <object class="GtkCellRendererText" id="renderer1"/>
-                      <attributes>
-                        <attribute name="text">0</attribute>
-                      </attributes>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="filler">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="expand">False</property>
-        <property name="position">2</property>
-      </packing>
-    </child>
-    <child>
       <object class="GtkVBox" id="plugin_box">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <child>
           <placeholder/>
         </child>
       </object>
       <packing>
-        <property name="position">3</property>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">2</property>
       </packing>
     </child>
   </object>
diff --git a/data/ui/rhythmbox-ui.xml b/data/ui/rhythmbox-ui.xml
index ebf29ef..2aa169a 100644
--- a/data/ui/rhythmbox-ui.xml
+++ b/data/ui/rhythmbox-ui.xml
@@ -48,15 +48,14 @@
     </menu>
 
     <menu name="ViewMenu" action="View">
-      <menuitem name="ViewSmallDisplayMenu" action="ViewSmallDisplay"/>
       <menuitem name="ViewPartyModeMenu" action="ViewPartyMode"/>
       <placeholder name="ViewMenuModePlaceholder"/>
       <separator name="ViewSep1"/>
       <menuitem name="ViewSidePaneMenu" action="ViewSidePane"/>
       <menuitem name="ViewQueueAsSidebarMenu" action="ViewQueueAsSidebar"/>
-      <menuitem name="ViewBrowserMenu" action="ViewBrowser"/>
       <menuitem name="ViewStatusbarMenu" action="ViewStatusbar"/>
-      <menuitem name="ViewToolbarMenu" action="ViewToolbar"/>
+      <menuitem name="ViewSongPositionSliderMenu" action="ViewSongPositionSlider"/>
+      <menuitem name="ViewAlbumArtMenu" action="ViewAlbumArt"/>
       <separator name="ViewSep2"/>
       <menuitem name="ViewAllMenu" action="ViewAll"/>
       <separator name="ViewSep3"/>
@@ -96,8 +95,8 @@
   </menubar>
 
   <toolbar name="ToolBar">
-    <toolitem name="Play" action="ControlPlay"/>
     <toolitem name="Previous" action="ControlPrevious"/>
+    <toolitem name="Play" action="ControlPlay"/>
     <toolitem name="Next" action="ControlNext"/>
     <separator/>
     <toolitem name="Repeat" action="ControlRepeat"/>
diff --git a/doc/reference/rhythmbox-sections.txt b/doc/reference/rhythmbox-sections.txt
index 9d05793..fde68f5 100644
--- a/doc/reference/rhythmbox-sections.txt
+++ b/doc/reference/rhythmbox-sections.txt
@@ -943,8 +943,6 @@ RBHeader
 RBHeaderClass
 rb_header_new
 rb_header_set_show_position_slider
-rb_header_sync
-rb_header_sync_time
 <SUBSECTION Standard>
 RBHeaderPrivate
 RB_HEADER
diff --git a/shell/rb-shell-player.c b/shell/rb-shell-player.c
index d485c0e..dc1d7c4 100644
--- a/shell/rb-shell-player.c
+++ b/shell/rb-shell-player.c
@@ -36,7 +36,7 @@
  * and manages the various #RBPlayOrder instances.  It provides simple operations
  * such as next, previous, play/pause, and seek.
  *
- * When playing internet radio streams, it first attempts to read the straem URL
+ * When playing internet radio streams, it first attempts to read the stream URL
  * as a playlist.  If this succeeds, the URLs from the playlist are stored in a
  * list and tried in turn in case of errors.  If the playlist parsing fails, the
  * stream URL is played directly.
@@ -77,7 +77,6 @@
 #include "rb-library-source.h"
 #include "rb-util.h"
 #include "rb-play-order.h"
-#include "rb-statusbar.h"
 #include "rb-playlist-source.h"
 #include "rb-play-queue-source.h"
 #include "rhythmdb.h"
@@ -245,11 +244,12 @@ struct RBShellPlayerPrivate
 	GCancellable *parser_cancellable;
 
 	RBHeader *header_widget;
-	RBStatusbar *statusbar_widget;
 
 	GSettings *settings;
 	GSettings *ui_settings;
 
+	gboolean has_prev;
+	gboolean has_next;
 	gboolean mute;
 	float volume;
 
@@ -269,12 +269,14 @@ enum
 	PROP_PLAY_ORDER,
 	PROP_PLAYING,
 	PROP_VOLUME,
-	PROP_STATUSBAR,
+	PROP_HEADER,
 	PROP_QUEUE_SOURCE,
 	PROP_QUEUE_ONLY,
 	PROP_PLAYING_FROM_QUEUE,
 	PROP_PLAYER,
-	PROP_MUTE
+	PROP_MUTE,
+	PROP_HAS_NEXT,
+	PROP_HAS_PREV
 };
 
 enum
@@ -318,15 +320,12 @@ static GtkToggleActionEntry rb_shell_player_toggle_entries [] =
 	{ "ControlRepeat", GNOME_MEDIA_REPEAT, N_("_Repeat"), "<control>R",
 	  N_("Play first song again after all songs are played"),
 	  G_CALLBACK (rb_shell_player_repeat_changed_cb) },
-	{ "ViewSongPositionSlider", NULL, N_("_Song Position Slider"), NULL,
-	  N_("Change the visibility of the song position slider"),
-	  NULL, TRUE },
 };
 static guint rb_shell_player_n_toggle_entries = G_N_ELEMENTS (rb_shell_player_toggle_entries);
 
 static guint rb_shell_player_signals[LAST_SIGNAL] = { 0 };
 
-G_DEFINE_TYPE (RBShellPlayer, rb_shell_player, GTK_TYPE_HBOX)
+G_DEFINE_TYPE (RBShellPlayer, rb_shell_player, G_TYPE_OBJECT)
 
 static void
 rb_shell_player_class_init (RBShellPlayerClass *klass)
@@ -482,16 +481,16 @@ rb_shell_player_class_init (RBShellPlayerClass *klass)
 							     G_PARAM_READWRITE));
 
 	/**
-	 * RBShellPlayer:statusbar:
+	 * RBShellPlayer:header:
 	 *
-	 * The #RBStatusbar object
+	 * The #RBHeader object
 	 */
 	g_object_class_install_property (object_class,
-					 PROP_STATUSBAR,
-					 g_param_spec_object ("statusbar",
-							      "RBStatusbar",
-							      "RBStatusbar object",
-							      RB_TYPE_STATUSBAR,
+					 PROP_HEADER,
+					 g_param_spec_object ("header",
+							      "RBHeader",
+							      "RBHeader object",
+							      RB_TYPE_HEADER,
 							      G_PARAM_READWRITE));
 	/**
 	 * RBShellPlayer:mute:
@@ -505,6 +504,30 @@ rb_shell_player_class_init (RBShellPlayerClass *klass)
 							       "Whether playback is muted",
 							       FALSE,
 							       G_PARAM_READWRITE));
+	/**
+	 * RBShellPlayer:has-next:
+	 *
+	 * Whether there is a track to play after the current track.
+	 */
+	g_object_class_install_property (object_class,
+					 PROP_HAS_NEXT,
+					 g_param_spec_boolean ("has-next",
+							       "has-next",
+							       "Whether there is a next track",
+							       FALSE,
+							       G_PARAM_READABLE));
+	/**
+	 * RBShellPlayer:has-prev:
+	 *
+	 * Whether there was a previous track before the current track.
+	 */
+	g_object_class_install_property (object_class,
+					 PROP_HAS_PREV,
+					 g_param_spec_boolean ("has-prev",
+							       "has-prev",
+							       "Whether there is a previous track",
+							       FALSE,
+							       G_PARAM_READABLE));
 
 	/**
 	 * RBShellPlayer::window-title-changed:
@@ -682,26 +705,15 @@ rb_shell_player_constructed (GObject *object)
 	player_settings_changed_cb (player->priv->settings, "transition-time", player);
 	player_settings_changed_cb (player->priv->settings, "play-order", player);
 
-	player->priv->header_widget = rb_header_new (player, player->priv->db);
-	gtk_widget_show (GTK_WIDGET (player->priv->header_widget));
-	gtk_box_pack_start (GTK_BOX (player), GTK_WIDGET (player->priv->header_widget), TRUE, TRUE, 0);
-	g_signal_connect_object (player->priv->header_widget,
-				 "notify::slider-dragging",
-				 G_CALLBACK (rb_shell_player_slider_dragging_cb),
-				 player, 0);
-	g_settings_bind (player->priv->ui_settings, "show-song-position-slider",
-			 player->priv->header_widget, "show-position-slider",
-			 G_SETTINGS_BIND_INVERT_BOOLEAN | G_SETTINGS_BIND_NO_SENSITIVITY);
-	action = gtk_action_group_get_action (player->priv->actiongroup,
-					      "ViewSongPositionSlider");
-	g_settings_bind (player->priv->ui_settings, "show-song-position-slider",
-			 action, "active",
-			 G_SETTINGS_BIND_INVERT_BOOLEAN | G_SETTINGS_BIND_NO_SENSITIVITY);
-
 	action = gtk_action_group_get_action (player->priv->actiongroup,
 					      "ControlPlay");
 	g_object_set (action, "is-important", TRUE, NULL);
 
+	action = gtk_action_group_get_action (player->priv->actiongroup, "ControlPrevious");
+	g_object_bind_property (player, "has-prev", action, "sensitive", G_BINDING_DEFAULT);
+	action = gtk_action_group_get_action (player->priv->actiongroup, "ControlNext");
+	g_object_bind_property (player, "has-next", action, "sensitive", G_BINDING_DEFAULT);
+
 	player->priv->syncing_state = TRUE;
 	rb_shell_player_set_playing_source (player, NULL);
 	rb_shell_player_sync_play_order (player);
@@ -1025,9 +1037,6 @@ rb_shell_player_init (RBShellPlayer *player)
 		exit (1);
 	}
 
-	gtk_box_set_spacing (GTK_BOX (player), 12);
-	gtk_container_set_border_width (GTK_CONTAINER (player), 3);
-
 	g_signal_connect_object (player->priv->mmplayer,
 				 "eos",
 				 G_CALLBACK (rb_shell_player_handle_eos),
@@ -1332,8 +1341,12 @@ rb_shell_player_set_property (GObject *object,
 		player->priv->volume = g_value_get_float (value);
 		rb_shell_player_sync_volume (player, FALSE, TRUE);
 		break;
-	case PROP_STATUSBAR:
-		player->priv->statusbar_widget = g_value_get_object (value);
+	case PROP_HEADER:
+		player->priv->header_widget = g_value_get_object (value);
+		g_signal_connect_object (player->priv->header_widget,
+					 "notify::slider-dragging",
+					 G_CALLBACK (rb_shell_player_slider_dragging_cb),
+					 player, 0);
 		break;
 	case PROP_QUEUE_SOURCE:
 		rb_shell_player_set_queue_source_internal (player, g_value_get_object (value));
@@ -1389,8 +1402,8 @@ rb_shell_player_get_property (GObject *object,
 	case PROP_VOLUME:
 		g_value_set_float (value, player->priv->volume);
 		break;
-	case PROP_STATUSBAR:
-		g_value_set_object (value, player->priv->statusbar_widget);
+	case PROP_HEADER:
+		g_value_set_object (value, player->priv->header_widget);
 		break;
 	case PROP_QUEUE_SOURCE:
 		g_value_set_object (value, player->priv->queue_source);
@@ -1407,6 +1420,12 @@ rb_shell_player_get_property (GObject *object,
 	case PROP_MUTE:
 		g_value_set_boolean (value, player->priv->mute);
 		break;
+	case PROP_HAS_NEXT:
+		g_value_set_boolean (value, player->priv->has_next);
+		break;
+	case PROP_HAS_PREV:
+		g_value_set_boolean (value, player->priv->has_prev);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1924,22 +1943,21 @@ rb_shell_player_sync_play_order (RBShellPlayer *player)
 
 static void
 rb_shell_player_play_order_update_cb (RBPlayOrder *porder,
-				      gboolean has_next,
-				      gboolean has_previous,
+				      gboolean _has_next,
+				      gboolean _has_previous,
 				      RBShellPlayer *player)
 {
 	/* we cannot depend on the values of has_next, has_previous or porder
 	 * since this can be called for the main porder, queue porder, etc
 	 */
-	gboolean have_next = FALSE;
-	gboolean have_previous = FALSE;
-	GtkAction *action;
+	gboolean has_next = FALSE;
+	gboolean has_prev = FALSE;
 	RhythmDBEntry *entry;
 
 	entry = rb_shell_player_get_playing_entry (player);
 	if (entry != NULL) {
-		have_next = TRUE;
-		have_previous = TRUE;
+		has_next = TRUE;
+		has_prev = TRUE;
 		rhythmdb_entry_unref (entry);
 	} else {
 		if (player->priv->current_playing_source &&
@@ -1948,21 +1966,23 @@ rb_shell_player_play_order_update_cb (RBPlayOrder *porder,
 			g_object_get (player->priv->current_playing_source, "play-order", &porder, NULL);
 			if (porder == NULL)
 				porder = g_object_ref (player->priv->play_order);
-			have_next = rb_play_order_has_next (porder);
+			has_next = rb_play_order_has_next (porder);
 			g_object_unref (porder);
 		}
 		if (player->priv->queue_play_order) {
-			have_next |= rb_play_order_has_next (player->priv->queue_play_order);
+			has_next |= rb_play_order_has_next (player->priv->queue_play_order);
 		}
-		have_previous = (player->priv->current_playing_source != NULL);
+		has_prev = (player->priv->current_playing_source != NULL);
 	}
 
-	action = gtk_action_group_get_action (player->priv->actiongroup,
-					      "ControlPrevious");
-	g_object_set (action, "sensitive", have_previous, NULL);
-	action = gtk_action_group_get_action (player->priv->actiongroup,
-					      "ControlNext");
-	g_object_set (action, "sensitive", have_next, NULL);
+	if (has_prev != player->priv->has_prev) {
+		player->priv->has_prev = has_prev;
+		g_object_notify (G_OBJECT (player), "has-prev");
+	}
+	if (has_next != player->priv->has_next) {
+		player->priv->has_next = has_next;
+		g_object_notify (G_OBJECT (player), "has-next");
+	}
 }
 
 /**
@@ -2506,7 +2526,12 @@ rb_shell_player_sync_volume (RBShellPlayer *player,
 				      player->priv->mute ? 0.0 : player->priv->volume);
 	}
 
-	rb_settings_delayed_sync (player->priv->settings, (RBDelayedSyncFunc) sync_volume_cb, g_object_ref (player), g_object_unref);
+	if (player->priv->syncing_state == FALSE) {
+		rb_settings_delayed_sync (player->priv->settings,
+					  (RBDelayedSyncFunc) sync_volume_cb,
+					  g_object_ref (player),
+					  g_object_unref);
+	}
 
 	entry = rb_shell_player_get_playing_entry (player);
 	if (entry != NULL) {
@@ -2994,7 +3019,6 @@ rb_shell_player_sync_buttons (RBShellPlayer *player)
 {
 	GtkAction *action;
 	RBSource *source;
-	gboolean not_small;
 	RBEntryView *view;
 	int entry_view_state;
 	RhythmDBEntry *entry;
@@ -3013,10 +3037,9 @@ rb_shell_player_sync_buttons (RBShellPlayer *player)
 
 	rb_debug ("syncing with source %p", source);
 
-        not_small = !g_settings_get_boolean (player->priv->ui_settings, "small-display");
 	action = gtk_action_group_get_action (player->priv->actiongroup,
 					      "ViewJumpToPlaying");
-	g_object_set (action, "sensitive", entry != NULL && not_small, NULL);
+	g_object_set (action, "sensitive", entry != NULL, NULL);
 
 	action = gtk_action_group_get_action (player->priv->actiongroup,
 					      "ControlPlay");
@@ -3751,6 +3774,7 @@ player_image_cb (RBPlayer *player,
 	g_value_unset (&v);
 
 	g_object_unref (store);
+	rb_ext_db_key_free (key);
 }
 
 /**
diff --git a/shell/rb-shell-player.h b/shell/rb-shell-player.h
index f98fe0b..217e7df 100644
--- a/shell/rb-shell-player.h
+++ b/shell/rb-shell-player.h
@@ -26,7 +26,7 @@
  *
  */
 
-#include <gtk/gtk.h>
+#include <glib-object.h>
 
 #include <sources/rb-source.h>
 #include <rhythmdb/rhythmdb.h>
@@ -64,14 +64,14 @@ typedef struct RBShellPlayerPrivate RBShellPlayerPrivate;
 
 struct _RBShellPlayer
 {
-	GtkHBox parent;
+	GObject parent;
 
 	RBShellPlayerPrivate *priv;
 };
 
 struct _RBShellPlayerClass
 {
-	GtkHBoxClass parent_class;
+	GObjectClass parent_class;
 
 	void (*window_title_changed) (RBShellPlayer *player, const char *window_title);
 	void (*elapsed_changed) (RBShellPlayer *player, guint elapsed);
diff --git a/shell/rb-shell-preferences.c b/shell/rb-shell-preferences.c
index 1fa506c..ecde4e2 100644
--- a/shell/rb-shell-preferences.c
+++ b/shell/rb-shell-preferences.c
@@ -77,8 +77,6 @@ void rb_shell_preferences_column_check_changed_cb (GtkCheckButton *butt,
 						   RBShellPreferences *shell_preferences);
 void rb_shell_preferences_browser_views_activated_cb (GtkWidget *widget,
 						      RBShellPreferences *shell_preferences);
-static gboolean toolbar_style_get_map (GValue *value, GVariant *variant, gpointer data);
-static GVariant *toolbar_style_set_map (const GValue *value, const GVariantType *variant_type, gpointer data);
 
 static void column_check_toggled_cb (GtkWidget *widget, RBShellPreferences *preferences);
 
@@ -127,8 +125,6 @@ struct RBShellPreferencesPrivate
 
 	GSList *browser_views_group;
 
-	GtkWidget *toolbar_style_menu;
-
 	gboolean applying_settings;
 
 	GSettings *main_settings;
@@ -261,23 +257,8 @@ rb_shell_preferences_init (RBShellPreferences *shell_preferences)
 				    "browser-views",
 				    shell_preferences);
 
-	/* toolbar button style */
-	rb_builder_boldify_label (builder, "toolbar_style_label");
-	shell_preferences->priv->toolbar_style_menu =
-		GTK_WIDGET (gtk_builder_get_object (builder, "toolbar_style_menu"));
-	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (shell_preferences->priv->toolbar_style_menu),
-					      rb_combo_box_hyphen_separator_func,
-					      NULL, NULL);
-
 	shell_preferences->priv->main_settings = g_settings_new ("org.gnome.rhythmbox");
 
-	g_settings_bind_with_mapping (shell_preferences->priv->main_settings, "toolbar-style",
-				      shell_preferences->priv->toolbar_style_menu, "active",
-				      G_SETTINGS_BIND_DEFAULT,
-				      (GSettingsBindGetMapping) toolbar_style_get_map,
-				      (GSettingsBindSetMapping) toolbar_style_set_map,
-				      NULL, NULL);
-
 	/* box for stuff added by plugins */
 	shell_preferences->priv->general_prefs_plugin_box =
 		GTK_WIDGET (gtk_builder_get_object (builder, "plugin_box"));
@@ -497,33 +478,6 @@ column_check_toggled_cb (GtkWidget *widget, RBShellPreferences *preferences)
 	g_variant_builder_unref (b);
 }
 
-static GVariant *
-toolbar_style_set_map (const GValue *value,
-		       const GVariantType *expected_type,
-		       gpointer data)
-{
-	int index = g_value_get_int (value);
-
-	/* ignore the separator row */
-	if (index >= 1)
-		index--;
-
-	return g_variant_new_int32 (index);
-}
-
-static gboolean
-toolbar_style_get_map (GValue *value, GVariant *variant, gpointer data)
-{
-	int index = g_variant_get_int32 (variant);
-
-	/* skip the separator row */
-	if (index >= 1)
-		index++;
-
-	g_value_set_int (value, index);
-	return TRUE;
-}
-
 /**
  * rb_shell_preferences_browser_views_activated_cb:
  * @widget: the radio button that was selected
diff --git a/shell/rb-shell.c b/shell/rb-shell.c
index 3603131..347daf4 100644
--- a/shell/rb-shell.c
+++ b/shell/rb-shell.c
@@ -92,6 +92,7 @@
 #include "rb-song-info.h"
 #include "rb-marshal.h"
 #include "rb-missing-plugins.h"
+#include "rb-header.h"
 #include "rb-podcast-manager.h"
 #include "rb-podcast-main-source.h"
 #include "rb-podcast-entry-types.h"
@@ -182,15 +183,11 @@ static void rb_shell_cmd_view_all (GtkAction *action,
 				   RBShell *shell);
 static void rb_shell_view_party_mode_changed_cb (GtkAction *action,
 						 RBShell *shell);
-static void rb_shell_view_smalldisplay_changed_cb (GtkAction *action,
-						 RBShell *shell);
 static void rb_shell_view_statusbar_changed_cb (GtkAction *action,
 						RBShell *shell);
 static void rb_shell_view_queue_as_sidebar_changed_cb (GtkAction *action,
 						       RBShell *shell);
 static void rb_shell_load_complete_cb (RhythmDB *db, RBShell *shell);
-static void rb_shell_sync_toolbar_state (RBShell *shell);
-static void rb_shell_sync_smalldisplay (RBShell *shell);
 static void rb_shell_sync_pane_visibility (RBShell *shell);
 static void rb_shell_sync_statusbar_visibility (RBShell *shell);
 static void rb_shell_set_visibility (RBShell *shell,
@@ -210,7 +207,6 @@ static void rb_shell_volume_widget_changed_cb (GtkScaleButton *vol,
 static void rb_shell_player_volume_changed_cb (RBShellPlayer *player,
 					       GParamSpec *arg,
 					       RBShell *shell);
-static void settings_changed_cb (GSettings *settings, const char *key, RBShell *shell);
 
 static void rb_shell_session_init (RBShell *shell);
 
@@ -305,6 +301,7 @@ struct _RBShellPrivate
 
 	RBShellPlayer *player_shell;
 	RBShellClipboard *clipboard_shell;
+	RBHeader *header;
 	RBStatusbar *statusbar;
 	RBPlaylistManager *playlist_manager;
 	RBRemovableMediaManager *removable_media_manager;
@@ -328,12 +325,6 @@ struct _RBShellPrivate
 	char *cached_title;
 	gboolean cached_playing;
 
-	guint sidepane_visibility_notify_id;
-	guint toolbar_visibility_notify_id;
-	guint toolbar_style_notify_id;
-	guint smalldisplay_notify_id;
-
-	glong last_small_time; /* when we last changed small mode */
 	gboolean party_mode;
 
 	GSettings *settings;
@@ -388,12 +379,6 @@ static GtkToggleActionEntry rb_shell_toggle_entries [] =
 	{ "ViewSidePane", NULL, N_("Side _Pane"), "F9",
 	  N_("Change the visibility of the side pane"),
 	  NULL, TRUE },
-	{ "ViewToolbar", NULL, N_("T_oolbar"), NULL,
-	  N_("Change the visibility of the toolbar"),
-	  NULL, TRUE },
-	{ "ViewSmallDisplay", NULL, N_("_Small Display"), "<control>D",
-	  N_("Make the main window smaller"),
-	  G_CALLBACK (rb_shell_view_smalldisplay_changed_cb), },
 	{ "ViewPartyMode", NULL, N_("Party _Mode"), "F11",
 	  N_("Change the status of the party mode"),
 	  G_CALLBACK (rb_shell_view_party_mode_changed_cb), FALSE },
@@ -403,6 +388,12 @@ static GtkToggleActionEntry rb_shell_toggle_entries [] =
         { "ViewStatusbar", NULL, N_("S_tatusbar"), NULL,
 	  N_("Change the visibility of the statusbar"),
 	  G_CALLBACK (rb_shell_view_statusbar_changed_cb), TRUE },
+	{ "ViewSongPositionSlider", NULL, N_("_Song Position Slider"), NULL,
+	  N_("Change the visibility of the song position slider"),
+	  NULL, TRUE },
+	{ "ViewAlbumArt", NULL, N_("_Album Art"), NULL,
+	  N_("Change the visibility of the album art display"),
+	  NULL, TRUE },
         { "ViewBrowser", NULL, N_("_Browse"), "<control>B",
 	  N_("Change the visibility of the browser"),
 	  NULL, TRUE }
@@ -508,6 +499,7 @@ load_external_art_cb (RBExtDB *store, GValue *value, RBShell *shell)
 	if (error != NULL) {
 		rb_debug ("unable to load pixbuf: %s", error->message);
 		g_clear_error (&error);
+		g_object_unref (loader);
 		return NULL;
 	}
 
@@ -674,10 +666,14 @@ construct_widgets (RBShell *shell)
 	g_object_get (shell->priv->display_page_tree, "model", &shell->priv->display_page_model, NULL);
 	rb_display_page_group_add_core_groups (G_OBJECT (shell), shell->priv->display_page_model);
 
+	shell->priv->header = rb_header_new (shell->priv->player_shell, shell->priv->db);
+	g_object_set (shell->priv->player_shell, "header", shell->priv->header, NULL);
+	gtk_widget_show (GTK_WIDGET (shell->priv->header));
+	g_settings_bind (shell->priv->settings, "time-display", shell->priv->header, "show-remaining", G_SETTINGS_BIND_DEFAULT);
+
 	shell->priv->statusbar = rb_statusbar_new (shell->priv->db,
 						   shell->priv->ui_manager,
 						   shell->priv->track_transfer_queue);
-	g_object_set (shell->priv->player_shell, "statusbar", shell->priv->statusbar, NULL);
 	gtk_widget_show (GTK_WIDGET (shell->priv->statusbar));
 
 	g_signal_connect_object (shell->priv->display_page_tree, "selected",
@@ -764,8 +760,6 @@ construct_widgets (RBShell *shell)
 
 	shell->priv->main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
 	gtk_container_set_border_width (GTK_CONTAINER (shell->priv->main_vbox), 0);
-	gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->player_shell), FALSE, TRUE, 6);
-	gtk_widget_show (GTK_WIDGET (shell->priv->player_shell));
 
 	gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->top_container), FALSE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), shell->priv->paned, TRUE, TRUE, 0);
@@ -868,9 +862,7 @@ construct_load_ui (RBShell *shell)
 	toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
 	gtk_style_context_add_class (gtk_widget_get_style_context (toolbar),
 				     GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
-	g_settings_bind (shell->priv->settings, "toolbar-visible",
-			 toolbar, "visible",
-			 G_SETTINGS_BIND_DEFAULT);
+	gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH);
 	gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), toolbar, FALSE, FALSE, 0);
 	gtk_box_reorder_child (GTK_BOX (shell->priv->main_vbox), toolbar, 1);
 
@@ -885,7 +877,8 @@ construct_load_ui (RBShell *shell)
 
 	tool_item = gtk_tool_item_new ();
 	gtk_tool_item_set_expand (tool_item, TRUE);
-	gtk_widget_show (GTK_WIDGET (tool_item));
+	gtk_container_add (GTK_CONTAINER (tool_item), GTK_WIDGET (shell->priv->header));
+	gtk_widget_show_all (GTK_WIDGET (tool_item));
 	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, -1);
 
 	tool_item = gtk_tool_item_new ();
@@ -1129,12 +1122,11 @@ rb_shell_startup (GApplication *app)
 {
 	RBShell *shell = RB_SHELL (app);
 	GtkAction *gtkaction;
+	RBEntryView *view;
 
 	rb_debug ("Constructing shell");
 	rb_profile_start ("constructing shell");
 
-	g_signal_connect_object (shell->priv->settings, "changed", G_CALLBACK (settings_changed_cb), shell, 0);
-
 	gtkaction = gtk_action_group_get_action (shell->priv->actiongroup, "ViewSidePane");
 	g_settings_bind (shell->priv->settings, "display-page-tree-visible",
 			 gtkaction, "active",
@@ -1143,10 +1135,21 @@ rb_shell_startup (GApplication *app)
 			 shell->priv->sidebar_container, "visible",
 			 G_SETTINGS_BIND_DEFAULT);
 
-	gtkaction = gtk_action_group_get_action (shell->priv->actiongroup, "ViewToolbar");
-	g_settings_bind (shell->priv->settings, "toolbar-visible",
+	gtkaction = gtk_action_group_get_action (shell->priv->actiongroup, "ViewSongPositionSlider");
+	g_settings_bind (shell->priv->settings, "show-song-position-slider",
 			 gtkaction, "active",
 			 G_SETTINGS_BIND_DEFAULT);
+	g_settings_bind (shell->priv->settings, "show-song-position-slider",
+			 shell->priv->header, "show-position-slider",
+			 G_SETTINGS_BIND_DEFAULT);
+
+	gtkaction = gtk_action_group_get_action (shell->priv->actiongroup, "ViewAlbumArt");
+	g_settings_bind (shell->priv->settings, "show-album-art",
+			 gtkaction, "active",
+			 G_SETTINGS_BIND_DEFAULT);
+	g_settings_bind (shell->priv->settings, "show-album-art",
+			 shell->priv->header, "show-album-art",
+			 G_SETTINGS_BIND_DEFAULT);
 
 	rb_debug ("shell: syncing with settings");
 	rb_shell_sync_pane_visibility (shell);
@@ -1161,9 +1164,7 @@ rb_shell_startup (GApplication *app)
 	construct_plugins (shell);
 
 	rb_shell_sync_window_state (shell, FALSE);
-	rb_shell_sync_smalldisplay (shell);
 	rb_shell_sync_party_mode (shell);
-	rb_shell_sync_toolbar_state (shell);
 
 	rb_shell_select_page (shell, RB_DISPLAY_PAGE (shell->priv->library_source));
 
@@ -1189,19 +1190,9 @@ rb_shell_startup (GApplication *app)
 
 	gdk_notify_startup_complete ();
 
-	/* focus play if small, the entry view if not */
-	if (g_settings_get_boolean (shell->priv->settings, "small-display")) {
-		GtkWidget *play_button;
-
-		play_button = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar/Play");
-		gtk_widget_grab_focus (play_button);
-	} else {
-		RBEntryView *view;
-
-		view = rb_source_get_entry_view (RB_SOURCE (shell->priv->library_source));
-		if (view != NULL) {
-			gtk_widget_grab_focus (GTK_WIDGET (view));
-		}
+	view = rb_source_get_entry_view (RB_SOURCE (shell->priv->library_source));
+	if (view != NULL) {
+		gtk_widget_grab_focus (GTK_WIDGET (view));
 	}
 
 	rb_profile_end ("constructing shell");
@@ -2148,7 +2139,7 @@ rb_shell_window_state_cb (GtkWidget *widget,
 	if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
 		gboolean maximised = ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0);
 
-		if (!g_settings_get_boolean (shell->priv->settings, "small-display")) {
+		if (maximised != g_settings_get_boolean (shell->priv->settings, "maximized")) {
 			g_settings_set_boolean (shell->priv->settings,
 						"maximized",
 						maximised);
@@ -2228,24 +2219,17 @@ static void
 sync_window_settings (GSettings *settings, RBShell *shell)
 {
 	int width, height;
+	int oldwidth, oldheight;
 	int oldx, oldy;
 	int x, y;
 	int pos;
 
 	gtk_window_get_size (GTK_WINDOW (shell->priv->window), &width, &height);
-	if (g_settings_get_boolean (shell->priv->settings, "small-display")) {
-		if (width != g_settings_get_int (shell->priv->settings, "small-width")) {
-			rb_debug ("storing small window width of %d", width);
-			g_settings_set_int (shell->priv->settings, "small-width", width);
-		}
-	} else {
-		int oldwidth, oldheight;
 
-		g_settings_get (shell->priv->settings, "size", "(ii)", &oldwidth, &oldheight);
-		if ((width != oldwidth) || (height != oldheight)) {
-			rb_debug ("storing window size of %d:%d", width, height);
-			g_settings_set (shell->priv->settings, "size", "(ii)", width, height);
-		}
+	g_settings_get (shell->priv->settings, "size", "(ii)", &oldwidth, &oldheight);
+	if ((width != oldwidth) || (height != oldheight)) {
+		rb_debug ("storing window size of %d:%d", width, height);
+		g_settings_set (shell->priv->settings, "size", "(ii)", width, height);
 	}
 
 	gtk_window_get_position (GTK_WINDOW(shell->priv->window), &x, &y);
@@ -2337,44 +2321,26 @@ rb_shell_sync_window_state (RBShell *shell,
 			    gboolean dont_maximise)
 {
 	GdkGeometry hints;
+	int width, height;
 	int x, y;
 
 	rb_profile_start ("syncing window state");
 
-	if (g_settings_get_boolean (shell->priv->settings, "small-display")) {
-		int width;
-
-		width = g_settings_get_int (shell->priv->settings, "small-width");
-		hints.min_height = -1;
-		hints.min_width = -1;
-		hints.max_height = -1;
-		hints.max_width = 3000;
-		gtk_window_set_default_size (GTK_WINDOW (shell->priv->window), width, 0);
-		gtk_window_resize (GTK_WINDOW (shell->priv->window), width, 1);
-		gtk_window_set_geometry_hints (GTK_WINDOW (shell->priv->window),
-						NULL,
-						&hints,
-						GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
-		gtk_window_unmaximize (GTK_WINDOW (shell->priv->window));
-		rb_debug ("syncing small window width to %d", width);
-	} else {
-		int width, height;
-		if (!dont_maximise) {
-			if (g_settings_get_boolean (shell->priv->settings, "maximized"))
-				gtk_window_maximize (GTK_WINDOW (shell->priv->window));
-			else
-				gtk_window_unmaximize (GTK_WINDOW (shell->priv->window));
-		}
+	if (!dont_maximise) {
+		if (g_settings_get_boolean (shell->priv->settings, "maximized"))
+			gtk_window_maximize (GTK_WINDOW (shell->priv->window));
+		else
+			gtk_window_unmaximize (GTK_WINDOW (shell->priv->window));
+	}
 
-		g_settings_get (shell->priv->settings, "size", "(ii)", &width, &height);
+	g_settings_get (shell->priv->settings, "size", "(ii)", &width, &height);
 
-		gtk_window_set_default_size (GTK_WINDOW (shell->priv->window), width, height);
-		gtk_window_resize (GTK_WINDOW (shell->priv->window), width, height);
-		gtk_window_set_geometry_hints (GTK_WINDOW (shell->priv->window),
-						NULL,
-						&hints,
-						0);
-	}
+	gtk_window_set_default_size (GTK_WINDOW (shell->priv->window), width, height);
+	gtk_window_resize (GTK_WINDOW (shell->priv->window), width, height);
+	gtk_window_set_geometry_hints (GTK_WINDOW (shell->priv->window),
+					NULL,
+					&hints,
+					0);
 
 	g_settings_get (shell->priv->settings, "position", "(ii)", &x, &y);
 	gtk_window_move (GTK_WINDOW (shell->priv->window), x, y);
@@ -2516,7 +2482,6 @@ rb_shell_playlist_created_cb (RBPlaylistManager *mgr,
 			      RBSource *source,
 			      RBShell *shell)
 {
-	g_settings_set_boolean (shell->priv->settings, "small-display", FALSE);
 	g_settings_set_boolean (shell->priv->settings, "display-page-tree-visible", TRUE);
 
 	rb_shell_sync_window_state (shell, FALSE);
@@ -2725,24 +2690,6 @@ rb_shell_set_window_title (RBShell *shell,
 }
 
 static void
-rb_shell_view_smalldisplay_changed_cb (GtkAction *action,
-				       RBShell *shell)
-{
-	GTimeVal time;
-
-	/* don't change more than once per second, it causes weirdness */
-	g_get_current_time (&time);
-	if (time.tv_sec == shell->priv->last_small_time)
-		return;
-
-	shell->priv->last_small_time = time.tv_sec;
-
-	g_settings_set_boolean (shell->priv->settings,
-				"small-display",
-				gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
-}
-
-static void
 rb_shell_view_statusbar_changed_cb (GtkAction *action,
 				    RBShell *shell)
 {
@@ -3149,47 +3096,6 @@ rb_shell_sync_pane_visibility (RBShell *shell)
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), queue_as_sidebar);
 }
 
-static void
-rb_shell_sync_toolbar_state (RBShell *shell)
-{
-	GtkWidget *toolbar;
-	guint toolbar_style;
-
-	toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
-
-	/* icons-only in small mode */
-	if (g_settings_get_boolean (shell->priv->settings, "small-display"))
-		toolbar_style = 3;
-	else
-		toolbar_style = g_settings_get_int (shell->priv->settings, "toolbar-style");
-
-	switch (toolbar_style) {
-	case 0:
-		/* default*/
-		gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
-		break;
-	case 1:
-		/* text below icons */
-		gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH);
-		break;
-	case 2:
-		/* text beside icons */
-		gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
-		break;
-	case 3:
-		/* icons only */
-		gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
-		break;
-	case 4:
-		/* text only */
-		gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_TEXT);
-		break;
-	default:
-		g_warning ("unknown toolbar style type");
-		gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
-	}
-}
-
 static gboolean
 window_state_event_cb (GtkWidget           *widget,
 		       GdkEventWindowState *event,
@@ -3213,8 +3119,6 @@ rb_shell_sync_party_mode (RBShell *shell)
 	/* disable/enable quit action */
 	action = gtk_action_group_get_action (shell->priv->actiongroup, "MusicQuit");
 	g_object_set (action, "sensitive", !shell->priv->party_mode, NULL);
-	action = gtk_action_group_get_action (shell->priv->actiongroup, "ViewSmallDisplay");
-	g_object_set (action, "sensitive", !shell->priv->party_mode, NULL);
 
 	/* show/hide queue as sidebar ? */
 
@@ -3240,55 +3144,6 @@ rb_shell_sync_party_mode (RBShell *shell)
 }
 
 static void
-rb_shell_sync_smalldisplay (RBShell *shell)
-{
-	GtkAction *action;
-	GtkAction *queue_action;
-	GtkAction *party_mode_action;
-	GtkAction *jump_to_playing_action;
-
-	rb_shell_sync_window_state (shell, FALSE);
-
-	action = gtk_action_group_get_action (shell->priv->actiongroup,
-					      "ViewSidePane");
-	queue_action = gtk_action_group_get_action (shell->priv->actiongroup,
-						    "ViewQueueAsSidebar");
-	party_mode_action = gtk_action_group_get_action (shell->priv->actiongroup,
-							 "ViewPartyMode");
-	jump_to_playing_action = gtk_action_group_get_action (shell->priv->actiongroup,
-							      "ViewJumpToPlaying");
-
-	if (g_settings_get_boolean (shell->priv->settings, "small-display")) {
-		g_object_set (action, "sensitive", FALSE, NULL);
-		g_object_set (queue_action, "sensitive", FALSE, NULL);
-		g_object_set (party_mode_action, "sensitive", FALSE, NULL);
-		g_object_set (jump_to_playing_action, "sensitive", FALSE, NULL);
-
-		gtk_widget_hide (GTK_WIDGET (shell->priv->paned));
-	} else {
-		RhythmDBEntry *playing;
-
-		g_object_set (action, "sensitive", TRUE, NULL);
-		g_object_set (queue_action, "sensitive", TRUE, NULL);
-		g_object_set (party_mode_action, "sensitive", TRUE, NULL);
-
-		playing = rb_shell_player_get_playing_entry (shell->priv->player_shell);
-		g_object_set (jump_to_playing_action, "sensitive", playing != NULL, NULL);
-		if (playing)
-			rhythmdb_entry_unref (playing);
-
-		gtk_widget_show (GTK_WIDGET (shell->priv->paned));
-	}
-	rb_shell_sync_statusbar_visibility (shell);
-	rb_shell_sync_toolbar_state (shell);
-
-	action = gtk_action_group_get_action (shell->priv->actiongroup,
-					      "ViewSmallDisplay");
-	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
-				      g_settings_get_boolean (shell->priv->settings, "small-display"));
-}
-
-static void
 rb_shell_sync_statusbar_visibility (RBShell *shell)
 {
 	gboolean visible;
@@ -3299,20 +3154,7 @@ rb_shell_sync_statusbar_visibility (RBShell *shell)
 	action = gtk_action_group_get_action (shell->priv->actiongroup, "ViewStatusbar");
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
 
-	gtk_widget_set_visible (GTK_WIDGET (shell->priv->statusbar),
-				visible && !g_settings_get_boolean (shell->priv->settings, "small-display"));
-}
-
-static void
-settings_changed_cb (GSettings *settings, const char *key, RBShell *shell)
-{
-	if (g_strcmp0 (key, "toolbar-style") == 0) {
-		rb_debug ("toolbar state changed");
-		rb_shell_sync_toolbar_state (shell);
-	} else if (g_strcmp0 (key, "small-display") == 0) {
-		rb_debug ("small display mode changed");
-		rb_shell_sync_smalldisplay (shell);
-	}
+	gtk_widget_set_visible (GTK_WIDGET (shell->priv->statusbar), visible);
 }
 
 static void
diff --git a/widgets/rb-header.c b/widgets/rb-header.c
index d18abbf..c62539f 100644
--- a/widgets/rb-header.c
+++ b/widgets/rb-header.c
@@ -43,6 +43,9 @@
 #include "rhythmdb.h"
 #include "rb-player.h"
 #include "rb-text-helpers.h"
+#include "rb-fading-image.h"
+#include "rb-file-helpers.h"
+#include "rb-ext-db.h"
 
 /**
  * SECTION:rb-header
@@ -60,6 +63,7 @@
 
 static void rb_header_class_init (RBHeaderClass *klass);
 static void rb_header_init (RBHeader *header);
+static void rb_header_dispose (GObject *object);
 static void rb_header_finalize (GObject *object);
 static void rb_header_set_property (GObject *object,
 				    guint prop_id,
@@ -69,6 +73,7 @@ static void rb_header_get_property (GObject *object,
 				    guint prop_id,
 				    GValue *value,
 				    GParamSpec *pspec);
+static void rb_header_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
 static void rb_header_update_elapsed (RBHeader *header);
 static void apply_slider_position (RBHeader *header);
 static gboolean slider_press_callback (GtkWidget *widget, GdkEventButton *event, RBHeader *header);
@@ -76,23 +81,29 @@ static gboolean slider_moved_callback (GtkWidget *widget, GdkEventMotion *event,
 static gboolean slider_release_callback (GtkWidget *widget, GdkEventButton *event, RBHeader *header);
 static void slider_changed_callback (GtkWidget *widget, RBHeader *header);
 static gboolean slider_scroll_callback (GtkWidget *widget, GdkEventScroll *event, RBHeader *header);
+static void time_button_clicked_cb (GtkWidget *button, RBHeader *header);
 
 static void rb_header_elapsed_changed_cb (RBShellPlayer *player, gint64 elapsed, RBHeader *header);
 static void rb_header_extra_metadata_cb (RhythmDB *db, RhythmDBEntry *entry, const char *property_name, const GValue *metadata, RBHeader *header);
+static void rb_header_sync (RBHeader *header);
+static void rb_header_sync_time (RBHeader *header);
+
+static void uri_dropped_cb (RBFadingImage *image, const char *uri, RBHeader *header);
+static void pixbuf_dropped_cb (RBFadingImage *image, GdkPixbuf *pixbuf, RBHeader *header);
+static void image_button_press_cb (GtkWidget *widget, GdkEvent *event, RBHeader *header);
 
 struct RBHeaderPrivate
 {
 	RhythmDB *db;
 	RhythmDBEntry *entry;
+	RBExtDB *art_store;
 
 	RBShellPlayer *shell_player;
 
-	GtkWidget *image;
+	GtkWidget *songbox;
 	GtkWidget *song;
-
-	GtkWidget *timeline;
-	GtkWidget *scaleline;
-	gboolean show_remaining;
+	GtkWidget *details;
+	GtkWidget *image;
 
 	GtkWidget *scale;
 	GtkAdjustment *adjustment;
@@ -100,11 +111,15 @@ struct RBHeaderPrivate
 	gboolean slider_locked;
 	guint slider_moved_timeout;
 	long latest_set_time;
-	GtkWidget *elapsed;
+
+	GtkWidget *timebutton;
+	GtkWidget *timelabel;
 
 	gint64 elapsed_time;		/* nanoseconds */
+	gboolean show_remaining;
 	long duration;
 	gboolean seekable;
+	char *image_path;
 };
 
 enum
@@ -115,29 +130,37 @@ enum
 	PROP_SEEKABLE,
 	PROP_SLIDER_DRAGGING,
 	PROP_SHOW_REMAINING,
-	PROP_SHOW_POSITION_SLIDER
+	PROP_SHOW_POSITION_SLIDER,
+	PROP_SHOW_ALBUM_ART
 };
 
 #define TITLE_FORMAT  "<big><b>%s</b></big>"
 #define ALBUM_FORMAT  "<i>%s</i>"
 #define ARTIST_FORMAT "<i>%s</i>"
-#define STREAM_FORMAT "(%s)"
+#define STREAM_FORMAT "%s"
+
+/* unicode graphic characters, encoded in UTF-8 */
+static const char const *UNICODE_MIDDLE_DOT = "\xC2\xB7";
 
 #define SCROLL_UP_SEEK_OFFSET	5
 #define SCROLL_DOWN_SEEK_OFFSET -5
 
-G_DEFINE_TYPE (RBHeader, rb_header, GTK_TYPE_HBOX)
+G_DEFINE_TYPE (RBHeader, rb_header, GTK_TYPE_GRID)
 
 static void
 rb_header_class_init (RBHeaderClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+	object_class->dispose = rb_header_dispose;
 	object_class->finalize = rb_header_finalize;
 
 	object_class->set_property = rb_header_set_property;
 	object_class->get_property = rb_header_get_property;
 
+	widget_class->size_allocate = rb_header_size_allocate;
+
 	/**
 	 * RBHeader:db:
 	 *
@@ -214,6 +237,18 @@ rb_header_class_init (RBHeaderClass *klass)
 							       "whether to show the playback position slider",
 							       TRUE,
 							       G_PARAM_READWRITE));
+	/**
+	 * RBHeader:show-album-art:
+	 *
+	 * Whether to show the album art display widget.
+	 */
+	g_object_class_install_property (object_class,
+					 PROP_SHOW_ALBUM_ART,
+					 g_param_spec_boolean ("show-album-art",
+							       "show album art",
+							       "whether to show album art",
+							       TRUE,
+							       G_PARAM_READWRITE));
 
 	g_type_class_add_private (klass, sizeof (RBHeaderPrivate));
 }
@@ -221,55 +256,16 @@ rb_header_class_init (RBHeaderClass *klass)
 static void
 rb_header_init (RBHeader *header)
 {
-	/*
-	 * The children in this widget look like this:
-	 * RBHeader
-	 *   GtkHBox
-	 *     GtkLabel			(priv->song)
-	 *   GtkHBox			(priv->timeline)
-	 *     GtkHScale		(priv->scale)
-	 *     GtkAlignment
-	 *       GtkLabel		(priv->elapsed)
-	 */
-	GtkWidget *hbox;
-	GtkWidget *vbox;
-
 	header->priv = G_TYPE_INSTANCE_GET_PRIVATE (header, RB_TYPE_HEADER, RBHeaderPrivate);
 
-	gtk_box_set_spacing (GTK_BOX (header), 3);
-
-	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-	gtk_widget_show (vbox);
-	gtk_box_pack_start (GTK_BOX (header), vbox, TRUE, TRUE, 0);
-
-	/* song info */
-	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 16);
-	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
-	gtk_widget_show (hbox);
-
-	header->priv->song = gtk_label_new ("");
- 	gtk_label_set_use_markup (GTK_LABEL (header->priv->song), TRUE);
- 	gtk_label_set_selectable (GTK_LABEL (header->priv->song), TRUE);
-	gtk_label_set_ellipsize (GTK_LABEL (header->priv->song), PANGO_ELLIPSIZE_END);
-	gtk_misc_set_alignment (GTK_MISC (header->priv->song), 0.0, 0.0);
-	gtk_box_pack_start (GTK_BOX (hbox), header->priv->song, TRUE, TRUE, 0);
-	gtk_widget_show (header->priv->song);
-
-	/* construct the time display */
-	header->priv->timeline = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
-	header->priv->elapsed = gtk_label_new ("");
-
-	gtk_misc_set_padding (GTK_MISC (header->priv->elapsed), 2, 0);
-	gtk_box_pack_start (GTK_BOX (header->priv->timeline), header->priv->elapsed, FALSE, FALSE, 0);
-	gtk_box_pack_end (GTK_BOX (hbox), header->priv->timeline, FALSE, FALSE, 0);
-	gtk_widget_show_all (header->priv->timeline);
-
-	/* row for the position slider */
-	header->priv->scaleline = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
-	gtk_box_pack_start (GTK_BOX (vbox), header->priv->scaleline, FALSE, FALSE, 0);
+	gtk_grid_set_column_spacing (GTK_GRID (header), 6);
+	gtk_grid_set_column_homogeneous (GTK_GRID (header), TRUE);
+	gtk_container_set_border_width (GTK_CONTAINER (header), 3);
 
+	/* set up position slider */
 	header->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 10.0, 1.0, 10.0, 0.0));
 	header->priv->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, header->priv->adjustment);
+	gtk_widget_set_hexpand (header->priv->scale, TRUE);
 	g_signal_connect_object (G_OBJECT (header->priv->scale),
 				 "button_press_event",
 				 G_CALLBACK (slider_press_callback),
@@ -292,7 +288,60 @@ rb_header_init (RBHeader *header)
 				 header, 0);
 	gtk_scale_set_draw_value (GTK_SCALE (header->priv->scale), FALSE);
 	gtk_widget_set_size_request (header->priv->scale, 150, -1);
-	gtk_box_pack_start (GTK_BOX (header->priv->scaleline), header->priv->scale, TRUE, TRUE, 0);
+
+	/* set up song information labels */
+	header->priv->songbox = gtk_grid_new ();
+	gtk_widget_set_hexpand (header->priv->songbox, TRUE);
+	gtk_widget_set_valign (header->priv->songbox, GTK_ALIGN_CENTER);
+
+	header->priv->song = gtk_label_new (" ");
+	gtk_label_set_use_markup (GTK_LABEL (header->priv->song), TRUE);
+	gtk_label_set_selectable (GTK_LABEL (header->priv->song), TRUE);
+	gtk_label_set_ellipsize (GTK_LABEL (header->priv->song), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (header->priv->song), 0.0, 0.5);
+	gtk_grid_attach (GTK_GRID (header->priv->songbox), header->priv->song, 0, 0, 1, 1);
+
+	header->priv->details = gtk_label_new ("");
+	gtk_label_set_use_markup (GTK_LABEL (header->priv->details), TRUE);
+	gtk_label_set_selectable (GTK_LABEL (header->priv->details), TRUE);
+	gtk_label_set_ellipsize (GTK_LABEL (header->priv->details), PANGO_ELLIPSIZE_END);
+	gtk_widget_set_hexpand (header->priv->details, TRUE);
+	gtk_misc_set_alignment (GTK_MISC (header->priv->details), 0.0, 0.5);
+	gtk_grid_attach (GTK_GRID (header->priv->songbox), header->priv->details, 0, 1, 1, 2);
+
+	/* elapsed time / duration display */
+	header->priv->timelabel = gtk_label_new ("");
+	gtk_widget_set_halign (header->priv->timelabel, GTK_ALIGN_END);
+
+	header->priv->timebutton = gtk_button_new ();
+	gtk_container_add (GTK_CONTAINER (header->priv->timebutton), header->priv->timelabel);
+	g_signal_connect_object (header->priv->timebutton,
+				 "clicked",
+				 G_CALLBACK (time_button_clicked_cb),
+				 header, 0);
+
+	/* image display */
+	header->priv->art_store = rb_ext_db_new ("album-art");
+	header->priv->image = GTK_WIDGET (g_object_new (RB_TYPE_FADING_IMAGE,
+							"fallback", RB_STOCK_MISSING_ARTWORK,
+							NULL));
+	g_signal_connect (header->priv->image,
+			  "pixbuf-dropped",
+			  G_CALLBACK (pixbuf_dropped_cb),
+			  header);
+	g_signal_connect (header->priv->image,
+			  "uri-dropped",
+			  G_CALLBACK (uri_dropped_cb),
+			  header);
+	g_signal_connect (header->priv->image,
+			  "button-press-event",
+			  G_CALLBACK (image_button_press_cb),
+			  header);
+
+	gtk_grid_attach (GTK_GRID (header), header->priv->image, 0, 0, 1, 1);
+	gtk_grid_attach (GTK_GRID (header), header->priv->songbox, 2, 0, 1, 1);
+	gtk_grid_attach (GTK_GRID (header), header->priv->timebutton, 3, 0, 1, 1);
+	gtk_grid_attach (GTK_GRID (header), header->priv->scale, 4, 0, 1, 1);
 
 	/* currently, nothing sets this.  it should be set on track changes. */
 	header->priv->seekable = TRUE;
@@ -301,6 +350,29 @@ rb_header_init (RBHeader *header)
 }
 
 static void
+rb_header_dispose (GObject *object)
+{
+	RBHeader *header = RB_HEADER (object);
+
+	if (header->priv->db != NULL) {
+		g_object_unref (header->priv->db);
+		header->priv->db = NULL;
+	}
+
+	if (header->priv->shell_player != NULL) {
+		g_object_unref (header->priv->shell_player);
+		header->priv->shell_player = NULL;
+	}
+
+	if (header->priv->art_store != NULL) {
+		g_object_unref (header->priv->art_store);
+		header->priv->art_store = NULL;
+	}
+
+	G_OBJECT_CLASS (rb_header_parent_class)->dispose (object);
+}
+
+static void
 rb_header_finalize (GObject *object)
 {
 	RBHeader *header;
@@ -311,9 +383,38 @@ rb_header_finalize (GObject *object)
 	header = RB_HEADER (object);
 	g_return_if_fail (header->priv != NULL);
 
+	g_free (header->priv->image_path);
+
 	G_OBJECT_CLASS (rb_header_parent_class)->finalize (object);
 }
 
+
+static void
+art_cb (RBExtDBKey *key, const char *filename, GValue *data, RBHeader *header)
+{
+	RhythmDBEntry *entry;
+
+	entry = rb_shell_player_get_playing_entry (header->priv->shell_player);
+	if (entry == NULL) {
+		return;
+	}
+
+	if (rhythmdb_entry_matches_ext_db_key (header->priv->db, entry, key)) {
+		GdkPixbuf *pixbuf = NULL;
+
+		if (data != NULL && G_VALUE_HOLDS (data, GDK_TYPE_PIXBUF)) {
+			pixbuf = GDK_PIXBUF (g_value_get_object (data));
+		}
+
+		rb_fading_image_set_pixbuf (RB_FADING_IMAGE (header->priv->image), pixbuf);
+
+		g_free (header->priv->image_path);
+		header->priv->image_path = g_strdup (filename);
+	}
+
+	rhythmdb_entry_unref (entry);
+}
+
 static void
 rb_header_playing_song_changed_cb (RBShellPlayer *player, RhythmDBEntry *entry, RBHeader *header)
 {
@@ -322,16 +423,109 @@ rb_header_playing_song_changed_cb (RBShellPlayer *player, RhythmDBEntry *entry,
 
 	header->priv->entry = entry;
 	if (header->priv->entry) {
+		RBExtDBKey *key;
+
 		header->priv->duration = rhythmdb_entry_get_ulong (header->priv->entry,
 								   RHYTHMDB_PROP_DURATION);
+
+		key = rhythmdb_entry_create_ext_db_key (entry, RHYTHMDB_PROP_ALBUM);
+		rb_ext_db_request (header->priv->art_store,
+				   key,
+				   (RBExtDBRequestCallback) art_cb,
+				   g_object_ref (header),
+				   g_object_unref);
+		rb_ext_db_key_free (key);
 	} else {
 		header->priv->duration = 0;
 	}
 
-	gtk_adjustment_set_upper (header->priv->adjustment, header->priv->duration);
-	gtk_adjustment_changed (header->priv->adjustment);
-
 	rb_header_sync (header);
+
+	g_free (header->priv->image_path);
+	header->priv->image_path = NULL;
+
+	rb_fading_image_start (RB_FADING_IMAGE (header->priv->image), 2000);
+}
+
+static void
+rb_header_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+	int spacing;
+	int scale_width;
+	int info_width;
+	int time_width;
+	int image_width;
+	GtkAllocation child_alloc;
+	gboolean rtl;
+
+	gtk_widget_set_allocation (widget, allocation);
+	spacing = gtk_grid_get_column_spacing (GTK_GRID (widget));
+	rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+
+	/* take some leading space for the image, which we always make square */
+	if (gtk_widget_get_visible (RB_HEADER (widget)->priv->image)) {
+		image_width = allocation->height;
+		if (rtl) {
+			child_alloc.x = allocation->x + allocation->width - image_width;
+			allocation->x -= image_width + spacing;
+		} else {
+			child_alloc.x = allocation->x;
+			allocation->x += image_width + spacing;
+		}
+		allocation->width -= image_width + spacing;
+		child_alloc.y = allocation->y;
+		child_alloc.width = image_width;
+		child_alloc.height = allocation->height;
+		gtk_widget_size_allocate (RB_HEADER (widget)->priv->image, &child_alloc);
+	}
+
+	/* figure out how much space to allocate to the scale.
+	 * it gets at least its minimum size, at most 1/3 of the
+	 * space we have.
+	 */
+	if (gtk_widget_get_visible (RB_HEADER (widget)->priv->scale)) {
+		gtk_widget_get_preferred_width (RB_HEADER (widget)->priv->scale, &scale_width, NULL);
+		if (scale_width < allocation->width / 3)
+			scale_width = allocation->width / 3;
+
+		if (rtl) {
+			child_alloc.x = allocation->x;
+		} else {
+			child_alloc.x = allocation->x + (allocation->width - scale_width) + spacing;
+		}
+		child_alloc.y = allocation->y;
+		child_alloc.width = scale_width - spacing;
+		child_alloc.height = allocation->height;
+		gtk_widget_size_allocate (RB_HEADER (widget)->priv->scale, &child_alloc);
+	} else {
+		scale_width = 0;
+	}
+
+	/* time button gets its minimum size */
+	gtk_widget_get_preferred_width (RB_HEADER (widget)->priv->songbox, NULL, &info_width);
+	gtk_widget_get_preferred_width (RB_HEADER (widget)->priv->timebutton, &time_width, NULL);
+
+	info_width = allocation->width - (scale_width + time_width) - (2 * spacing);
+
+	if (rtl) {
+		child_alloc.x = allocation->x + allocation->width - info_width;
+	} else {
+		child_alloc.x = allocation->x;
+	}
+	child_alloc.y = allocation->y;
+	child_alloc.width = info_width;
+	child_alloc.height = allocation->height;
+	gtk_widget_size_allocate (RB_HEADER (widget)->priv->songbox, &child_alloc);
+
+	if (rtl) {
+		child_alloc.x = allocation->x + scale_width + spacing;
+	} else {
+		child_alloc.x = allocation->x + info_width + spacing;
+	}
+	child_alloc.y = allocation->y;
+	child_alloc.width = time_width;
+	child_alloc.height = allocation->height;
+	gtk_widget_size_allocate (RB_HEADER (widget)->priv->timebutton, &child_alloc);
 }
 
 static void
@@ -369,7 +563,10 @@ rb_header_set_property (GObject *object,
 		rb_header_update_elapsed (header);
 		break;
 	case PROP_SHOW_POSITION_SLIDER:
-		gtk_widget_set_visible (header->priv->scaleline, g_value_get_boolean (value));
+		gtk_widget_set_visible (header->priv->scale, g_value_get_boolean (value));
+		break;
+	case PROP_SHOW_ALBUM_ART:
+		gtk_widget_set_visible (header->priv->image, g_value_get_boolean (value));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -402,7 +599,10 @@ rb_header_get_property (GObject *object,
 		g_value_set_boolean (value, header->priv->show_remaining);
 		break;
 	case PROP_SHOW_POSITION_SLIDER:
-		g_value_set_boolean (value, gtk_widget_get_visible (header->priv->scaleline));
+		g_value_set_boolean (value, gtk_widget_get_visible (header->priv->scale));
+		break;
+	case PROP_SHOW_ALBUM_ART:
+		g_value_set_boolean (value, gtk_widget_get_visible (header->priv->image));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -427,7 +627,7 @@ rb_header_new (RBShellPlayer *shell_player, RhythmDB *db)
 	header = RB_HEADER (g_object_new (RB_TYPE_HEADER,
 					  "shell-player", shell_player,
 					  "db", db,
-					  "spacing", 6, NULL));
+					  NULL));
 
 	g_return_val_if_fail (header->priv != NULL, NULL);
 
@@ -449,69 +649,7 @@ get_extra_metadata (RhythmDB *db, RhythmDBEntry *entry, const char *field, char
 	}
 }
 
-/* unicode graphic characters, encoded in UTF-8 */
-static const char const *UNICODE_MIDDLE_DOT = "\xC2\xB7";
-
-static char *
-write_header (PangoDirection native_dir,
-	      const char *title,
-	      const char *artist,
-	      const char *album,
-	      const char *stream)
-{
-	const char *by;
-	const char *from;
-	PangoDirection tags_dir;
-	PangoDirection header_dir;
-
-	if (!title)
-		title  = "";
-	if (!artist)
-		artist = "";
-	if (!album )
-		album  = "";
-	if (!stream)
-		stream = "";
-
-	tags_dir = rb_text_common_direction (title, artist, album, stream, NULL);
-
-	/* if the tags have a defined direction that conflicts with the native
-	 * direction, show them in their natural direction with a neutral
-	 * separator
-	 */
-	if (!rb_text_direction_conflict (tags_dir, native_dir)) {
-		header_dir = native_dir;
-		by = _("by");
-		from = _("from");
-	} else {
-		header_dir = tags_dir;
-		by = UNICODE_MIDDLE_DOT;
-		from = UNICODE_MIDDLE_DOT;
-	}
-
-	if (!artist[0])
-		by = "";
-	if (!album[0])
-		from = "";
-
-	return rb_text_cat (header_dir,
-			 title,  TITLE_FORMAT,
-			 by,     "%s",
-			 artist, ARTIST_FORMAT,
-			 from,   "%s",
-			 album,  ALBUM_FORMAT,
-			 stream, STREAM_FORMAT,
-			 NULL);
-}
-
-/**
- * rb_header_sync:
- * @header: the #RBHeader
- *
- * Updates the header widget to be consistent with the current playing entry
- * including all streaming metadata.
- */
-void
+static void
 rb_header_sync (RBHeader *header)
 {
 	char *label_text;
@@ -569,14 +707,52 @@ rb_header_sync (RBHeader *header)
 		widget_dir = (gtk_widget_get_direction (GTK_WIDGET (header->priv->song)) == GTK_TEXT_DIR_LTR) ?
 			     PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
 
-		label_text = write_header (widget_dir, title, artist, album, stream_name);
-
-		gtk_label_set_markup (GTK_LABEL (header->priv->song), label_text);
-		g_free (label_text);
+		char *t;
+		t = rb_text_cat (widget_dir, title, TITLE_FORMAT, NULL);
+		gtk_label_set_markup (GTK_LABEL (header->priv->song), t);
+		g_free (t);
+
+		if (artist == NULL || artist[0] == '\0') {	/* this is crap; should be up to the entry type */
+			if (stream_name != NULL) {
+				t = rb_text_cat (widget_dir, stream_name, STREAM_FORMAT, NULL);
+				gtk_label_set_markup (GTK_LABEL (header->priv->details), t);
+				g_free (t);
+			} else {
+				gtk_label_set_markup (GTK_LABEL (header->priv->details), "");
+			}
+		} else {
+			const char *by;
+			const char *from;
+			PangoDirection dir;
+			PangoDirection native;
+
+			native = PANGO_DIRECTION_LTR;
+			if (gtk_widget_get_direction (GTK_WIDGET (header->priv->details)) != GTK_TEXT_DIR_LTR) {
+				native = PANGO_DIRECTION_RTL;
+			}
+
+			dir = rb_text_common_direction (artist, album, NULL);
+			if (!rb_text_direction_conflict (dir, native)) {
+				dir = native;
+				by = _("by");
+				from = _("from");
+			} else {
+				by = UNICODE_MIDDLE_DOT;
+				from = UNICODE_MIDDLE_DOT;
+			}
+
+			t = rb_text_cat (dir,
+					 by, "%s",
+					 artist, ARTIST_FORMAT,
+					 from, "%s",
+					 album, ALBUM_FORMAT,
+					 NULL);
+			gtk_label_set_markup (GTK_LABEL (header->priv->details), t);
+			g_free (t);
+		}
 
-		gtk_widget_set_sensitive (header->priv->scaleline, have_duration && header->priv->seekable);
-		if (have_duration)
-			rb_header_sync_time (header);
+		gtk_widget_set_sensitive (header->priv->scale, have_duration && header->priv->seekable);
+		rb_header_sync_time (header);
 
 		g_free (streaming_artist);
 		g_free (streaming_album);
@@ -587,28 +763,13 @@ rb_header_sync (RBHeader *header)
 		gtk_label_set_markup (GTK_LABEL (header->priv->song), label_text);
 		g_free (label_text);
 
-		gtk_widget_set_sensitive (header->priv->scaleline, FALSE);
+		gtk_label_set_text (GTK_LABEL (header->priv->details), "");
 
-		header->priv->slider_locked = TRUE;
-		gtk_adjustment_set_value (header->priv->adjustment, 0.0);
-		header->priv->slider_locked = FALSE;
-		gtk_widget_set_sensitive (header->priv->scale, FALSE);
-
-		gtk_label_set_text (GTK_LABEL (header->priv->elapsed), "");
+		rb_header_sync_time (header);
 	}
 }
 
-/**
- * rb_header_sync_time:
- * @header: the #RBHeader
- *
- * Updates the time display components of the header.
- * If the position slider is being dragged, the display is not updated.
- * If the duration of the playing entry is known, the position slider is
- * updated along with the elapsed/remaining time display.  Otherwise,
- * the slider is made insensitive.
- */
-void
+static void
 rb_header_sync_time (RBHeader *header)
 {
 	if (header->priv->shell_player == NULL)
@@ -623,12 +784,22 @@ rb_header_sync_time (RBHeader *header)
 		double progress = ((double) header->priv->elapsed_time) / RB_PLAYER_SECOND;
 
 		header->priv->slider_locked = TRUE;
+
+		g_object_freeze_notify (G_OBJECT (header->priv->adjustment));
 		gtk_adjustment_set_value (header->priv->adjustment, progress);
+		gtk_adjustment_set_upper (header->priv->adjustment, header->priv->duration);
+		g_object_thaw_notify (G_OBJECT (header->priv->adjustment));
+
 		header->priv->slider_locked = FALSE;
 		gtk_widget_set_sensitive (header->priv->scale, header->priv->seekable);
 	} else {
 		header->priv->slider_locked = TRUE;
+
+		g_object_freeze_notify (G_OBJECT (header->priv->adjustment));
 		gtk_adjustment_set_value (header->priv->adjustment, 0.0);
+		gtk_adjustment_set_upper (header->priv->adjustment, 0.0);
+		g_object_thaw_notify (G_OBJECT (header->priv->adjustment));
+
 		header->priv->slider_locked = FALSE;
 		gtk_widget_set_sensitive (header->priv->scale, FALSE);
 	}
@@ -780,24 +951,49 @@ static void
 rb_header_update_elapsed (RBHeader *header)
 {
 	long seconds;
+	char *elapsed;
+	char *duration;
+	char *label;
 
-	/* sanity check */
-	seconds = header->priv->elapsed_time / RB_PLAYER_SECOND;
-	if (header->priv->duration > 0 && seconds > header->priv->duration)
+	if (header->priv->entry == NULL) {
+		gtk_label_set_text (GTK_LABEL (header->priv->timelabel), "");
 		return;
+	}
 
-	if (header->priv->entry != NULL) {
-		char *elapsed_text;
+	seconds = header->priv->elapsed_time / RB_PLAYER_SECOND;
+	if (header->priv->duration == 0) {
+		label = rb_make_time_string (seconds);
+		gtk_label_set_text (GTK_LABEL (header->priv->timelabel), label);
+		g_free (label);
+	} else if (header->priv->show_remaining) {
+
+		duration = rb_make_time_string (header->priv->duration);
+
+		if (seconds > header->priv->duration) {
+			elapsed = rb_make_time_string (0);
+		} else {
+			elapsed = rb_make_time_string (header->priv->duration - seconds);
+		}
 
-		elapsed_text = rb_make_elapsed_time_string (seconds,
-							    header->priv->duration,
-							    header->priv->show_remaining);
-		gtk_label_set_text (GTK_LABEL (header->priv->elapsed), elapsed_text);
-		g_free (elapsed_text);
+		/* Translators: remaining time / total time */
+		label = g_strdup_printf (_("-%s / %s"), elapsed, duration);
+		gtk_label_set_text (GTK_LABEL (header->priv->timelabel), label);
+
+		g_free (elapsed);
+		g_free (duration);
+		g_free (label);
 	} else {
-		gtk_label_set_text (GTK_LABEL (header->priv->elapsed), "");
-	}
+		elapsed = rb_make_time_string (seconds);
+		duration = rb_make_time_string (header->priv->duration);
 
+		/* Translators: elapsed time / total time */
+		label = g_strdup_printf (_("%s / %s"), elapsed, duration);
+		gtk_label_set_text (GTK_LABEL (header->priv->timelabel), label);
+
+		g_free (elapsed);
+		g_free (duration);
+		g_free (label);
+	}
 }
 
 static void
@@ -825,3 +1021,86 @@ rb_header_extra_metadata_cb (RhythmDB *db,
 		rb_header_sync (header);
 	}
 }
+
+static void
+pixbuf_dropped_cb (RBFadingImage *image, GdkPixbuf *pixbuf, RBHeader *header)
+{
+	RBExtDBKey *key;
+	const char *artist;
+	GValue v = G_VALUE_INIT;
+
+	if (header->priv->entry == NULL || pixbuf == NULL)
+		return;
+
+	/* maybe ignore tiny pixbufs? */
+
+	key = rb_ext_db_key_create_storage ("album", rhythmdb_entry_get_string (header->priv->entry, RHYTHMDB_PROP_ALBUM));
+	artist = rhythmdb_entry_get_string (header->priv->entry, RHYTHMDB_PROP_ALBUM_ARTIST);
+	if (artist == NULL || artist[0] == '\0' || strcmp (artist, _("Unknown")) == 0) {
+		artist = rhythmdb_entry_get_string (header->priv->entry, RHYTHMDB_PROP_ARTIST);
+	}
+	rb_ext_db_key_add_field (key, "artist", artist);
+
+	g_value_init (&v, GDK_TYPE_PIXBUF);
+	g_value_set_object (&v, image);
+	rb_ext_db_store (header->priv->art_store, key, RB_EXT_DB_SOURCE_USER_EXPLICIT, &v);
+	g_value_unset (&v);
+
+	rb_ext_db_key_free (key);
+}
+
+static void
+uri_dropped_cb (RBFadingImage *image, const char *uri, RBHeader *header)
+{
+	RBExtDBKey *key;
+	const char *artist;
+
+	if (header->priv->entry == NULL || uri == NULL)
+		return;
+
+	/* maybe ignore tiny pixbufs? */
+
+	key = rb_ext_db_key_create_storage ("album", rhythmdb_entry_get_string (header->priv->entry, RHYTHMDB_PROP_ALBUM));
+	artist = rhythmdb_entry_get_string (header->priv->entry, RHYTHMDB_PROP_ALBUM_ARTIST);
+	if (artist == NULL || artist[0] == '\0' || strcmp (artist, _("Unknown")) == 0) {
+		artist = rhythmdb_entry_get_string (header->priv->entry, RHYTHMDB_PROP_ARTIST);
+	}
+	rb_ext_db_key_add_field (key, "artist", artist);
+
+	rb_ext_db_store_uri (header->priv->art_store, key, RB_EXT_DB_SOURCE_USER_EXPLICIT, uri);
+
+	rb_ext_db_key_free (key);
+}
+
+static void
+image_button_press_cb (GtkWidget *widget, GdkEvent *event, RBHeader *header)
+{
+	if (event->button.type != GDK_2BUTTON_PRESS ||
+	    event->button.button != 1)
+		return;
+
+	if (header->priv->image_path != NULL) {
+		GAppInfo *app;
+		GAppLaunchContext *context;
+		GList *files = NULL;
+
+		app = g_app_info_get_default_for_type ("image/jpeg", FALSE);
+		if (app == NULL) {
+			return;
+		}
+
+		files = g_list_append (NULL, g_file_new_for_path (header->priv->image_path));
+
+		context = G_APP_LAUNCH_CONTEXT (gdk_display_get_app_launch_context (gtk_widget_get_display (widget)));
+		g_app_info_launch (app, files, context, NULL);
+		g_object_unref (context);
+		g_object_unref (app);
+		g_list_free_full (files, g_object_unref);
+	}
+}
+
+static void
+time_button_clicked_cb (GtkWidget *widget, RBHeader *header)
+{
+	g_object_set (header, "show-remaining", !header->priv->show_remaining, NULL);
+}
diff --git a/widgets/rb-header.h b/widgets/rb-header.h
index 20aeefa..6fd06bd 100644
--- a/widgets/rb-header.h
+++ b/widgets/rb-header.h
@@ -50,14 +50,14 @@ typedef struct RBHeaderPrivate RBHeaderPrivate;
 
 struct _RBHeader
 {
-	GtkHBox parent;
+	GtkGrid parent;
 
 	RBHeaderPrivate *priv;
 };
 
 struct _RBHeaderClass
 {
-	GtkHBoxClass parent;
+	GtkGridClass parent;
 };
 
 GType		rb_header_get_type		(void);
@@ -65,13 +65,6 @@ GType		rb_header_get_type		(void);
 RBHeader *	rb_header_new			(RBShellPlayer *shell_player,
 						 RhythmDB *db);
 
-void		rb_header_set_show_position_slider (RBHeader *header,
-                                                    gboolean show);
-
-void		rb_header_sync			(RBHeader *header);
-
-void		rb_header_sync_time		(RBHeader *header);
-
 G_END_DECLS
 
 #endif /* __RB_HEADER_H */



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