[gthumb: 9/13] media viewer: added keyboard shortcuts, actions to skip forward/backward



commit a7b8142ef4736b63d2ec5b6cbdc0bd80456db6ed
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Mon Jul 13 12:49:03 2020 +0200

    media viewer: added keyboard shortcuts, actions to skip forward/backward

 .../org.gnome.gthumb.gstreamer-tools.gschema.xml   |   3 +
 extensions/gstreamer_tools/actions.c               | 147 +++++++++
 extensions/gstreamer_tools/actions.h               |  12 +
 extensions/gstreamer_tools/callbacks.c             |  13 +
 extensions/gstreamer_tools/data/ui/mediabar.ui     | 220 ++++++++++---
 extensions/gstreamer_tools/gth-media-viewer-page.c | 348 +++++++++++++++++----
 extensions/gstreamer_tools/gth-media-viewer-page.h |   6 +
 extensions/gstreamer_tools/preferences.h           |   1 +
 8 files changed, 649 insertions(+), 101 deletions(-)
---
diff --git a/data/gschemas/org.gnome.gthumb.gstreamer-tools.gschema.xml 
b/data/gschemas/org.gnome.gthumb.gstreamer-tools.gschema.xml
index 91abd78a..30280d5f 100644
--- a/data/gschemas/org.gnome.gthumb.gstreamer-tools.gschema.xml
+++ b/data/gschemas/org.gnome.gthumb.gstreamer-tools.gschema.xml
@@ -25,6 +25,9 @@
     <key name="volume" type="i">
       <default>100</default>
     </key>
+    <key name="mute" type="b">
+      <default>false</default>
+    </key>
     <key name="use-hardware-acceleration" type="b">
       <default>false</default>
     </key>
diff --git a/extensions/gstreamer_tools/actions.c b/extensions/gstreamer_tools/actions.c
index 2b955f91..08e30d1d 100644
--- a/extensions/gstreamer_tools/actions.c
+++ b/extensions/gstreamer_tools/actions.c
@@ -207,6 +207,45 @@ gth_browser_activate_toggle_play (GSimpleAction    *action,
 }
 
 
+void
+gth_browser_activate_toggle_mute (GSimpleAction        *action,
+                                 GVariant      *parameter,
+                                 gpointer       user_data)
+{
+       GthBrowser              *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage      *page;
+
+       page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));
+       gth_media_viewer_page_toggle_mute (page);
+}
+
+
+void
+gth_browser_activate_play_faster (GSimpleAction        *action,
+                                 GVariant      *parameter,
+                                 gpointer       user_data)
+{
+       GthBrowser              *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage      *page;
+
+       page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));
+       gth_media_viewer_page_play_faster (page);
+}
+
+
+void
+gth_browser_activate_play_slower (GSimpleAction        *action,
+                                 GVariant      *parameter,
+                                 gpointer       user_data)
+{
+       GthBrowser              *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage      *page;
+
+       page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));
+       gth_media_viewer_page_play_slower (page);
+}
+
+
 void
 gth_browser_activate_video_zoom_fit (GSimpleAction     *action,
                                     GVariant           *state,
@@ -218,3 +257,111 @@ gth_browser_activate_video_zoom_fit (GSimpleAction        *action,
        g_simple_action_set_state (action, state);
        gth_media_viewer_page_set_fit_if_larger (page, g_variant_get_boolean (state));
 }
+
+
+void
+gth_browser_activate_next_video_frame (GSimpleAction   *action,
+                                      GVariant         *state,
+                                      gpointer          user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_next_frame (page);
+}
+
+
+void
+gth_browser_activate_skip_forward_smaller (GSimpleAction       *action,
+                                          GVariant             *state,
+                                          gpointer              user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, 5);
+}
+
+
+void
+gth_browser_activate_skip_forward_small (GSimpleAction *action,
+                                        GVariant               *state,
+                                        gpointer                user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, 10);
+}
+
+
+void
+gth_browser_activate_skip_forward_big (GSimpleAction   *action,
+                                      GVariant         *state,
+                                      gpointer          user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, 60);
+}
+
+
+void
+gth_browser_activate_skip_forward_bigger (GSimpleAction        *action,
+                                         GVariant              *state,
+                                         gpointer               user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, 60 * 5);
+}
+
+
+void
+gth_browser_activate_skip_back_smaller (GSimpleAction  *action,
+                                       GVariant                *state,
+                                       gpointer                 user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, -5);
+}
+
+
+void
+gth_browser_activate_skip_back_small (GSimpleAction    *action,
+                                     GVariant          *state,
+                                     gpointer           user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, -10);
+}
+
+
+void
+gth_browser_activate_skip_back_big (GSimpleAction      *action,
+                                   GVariant            *state,
+                                   gpointer             user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, -60);
+}
+
+
+void
+gth_browser_activate_skip_back_bigger (GSimpleAction   *action,
+                                      GVariant         *state,
+                                      gpointer          user_data)
+{
+       GthBrowser         *browser = GTH_BROWSER (user_data);
+       GthMediaViewerPage *page = GTH_MEDIA_VIEWER_PAGE (gth_browser_get_viewer_page (browser));;
+
+       gth_media_viewer_page_skip (page, -60 * 5);
+}
diff --git a/extensions/gstreamer_tools/actions.h b/extensions/gstreamer_tools/actions.h
index 8b2712b7..aa807c05 100644
--- a/extensions/gstreamer_tools/actions.h
+++ b/extensions/gstreamer_tools/actions.h
@@ -26,6 +26,18 @@
 
 DEF_ACTION_CALLBACK (gth_browser_activate_video_screenshot)
 DEF_ACTION_CALLBACK (gth_browser_activate_toggle_play)
+DEF_ACTION_CALLBACK (gth_browser_activate_toggle_mute)
+DEF_ACTION_CALLBACK (gth_browser_activate_play_faster)
+DEF_ACTION_CALLBACK (gth_browser_activate_play_slower)
 DEF_ACTION_CALLBACK (gth_browser_activate_video_zoom_fit)
+DEF_ACTION_CALLBACK (gth_browser_activate_next_video_frame)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_forward_smaller)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_forward_small)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_forward_big)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_forward_bigger)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_back_smaller)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_back_small)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_back_big)
+DEF_ACTION_CALLBACK (gth_browser_activate_skip_back_bigger)
 
 #endif /* ACTIONS_H */
diff --git a/extensions/gstreamer_tools/callbacks.c b/extensions/gstreamer_tools/callbacks.c
index bb5e4ebc..38b40017 100644
--- a/extensions/gstreamer_tools/callbacks.c
+++ b/extensions/gstreamer_tools/callbacks.c
@@ -31,6 +31,19 @@
 static const GthShortcut shortcuts[] = {
        { "video-screenshot", N_("Screenshot"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Alt>s" },
        { "toggle-play", N_("Play/Pause"), GTH_SHORTCUT_CONTEXT_VIEWER, GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, 
"space" },
+       { "toggle-mute", N_("Mute"), GTH_SHORTCUT_CONTEXT_VIEWER, GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "m" },
+       { "play-faster", N_("Play faster"), GTH_SHORTCUT_CONTEXT_VIEWER, GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, 
"plus" },
+       { "play-slower", N_("Play slower"), GTH_SHORTCUT_CONTEXT_VIEWER, GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, 
"minus" },
+       { "next-frame", N_("Next frame"), GTH_SHORTCUT_CONTEXT_VIEWER, GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, 
"period" },
+       { "skip-forward-smaller", N_("Go forward 5 seconds"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Shift>Right" },
+       { "skip-forward-small", N_("Go forward 10 seconds"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Alt>Right" },
+       { "skip-forward-big", N_("Go forward 1 minute"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Primary>Right" },
+       { "skip-forward-bigger", N_("Go forward 5 minutes"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Primary><Alt>Right" },
+       { "skip-back-smaller", N_("Go back 5 seconds"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Shift>Left" },
+       { "skip-back-small", N_("Go back 10 seconds"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Alt>Left" },
+       { "skip-back-big", N_("Go back 1 minute"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Primary>Left" },
+       { "skip-back-bigger", N_("Go back 5 minutes"), GTH_SHORTCUT_CONTEXT_VIEWER, 
GTH_SHORTCUT_CATEGORY_MEDIA_VIEWER, "<Primary><Alt>Left" },
+
 };
 
 
diff --git a/extensions/gstreamer_tools/data/ui/mediabar.ui b/extensions/gstreamer_tools/data/ui/mediabar.ui
index 42c7c727..036f5a39 100644
--- a/extensions/gstreamer_tools/data/ui/mediabar.ui
+++ b/extensions/gstreamer_tools/data/ui/mediabar.ui
@@ -1,17 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.20.0 -->
+<!-- Generated with glade 3.36.0 -->
 <interface>
-  <requires lib="gtk+" version="3.0"/>
+  <requires lib="gtk+" version="3.16"/>
   <object class="GtkAdjustment" id="position_adjustment">
     <property name="upper">100</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
   <object class="GtkAdjustment" id="volume_adjustment">
-    <property name="upper">1.5</property>
-    <property name="value">1</property>
-    <property name="step_increment">0.10000000000000001</property>
-    <property name="page_increment">0.10000000000000001</property>
+    <property name="upper">100</property>
+    <property name="value">100</property>
+    <property name="page_increment">5</property>
   </object>
   <object class="GtkBox" id="mediabar">
     <property name="visible">True</property>
@@ -159,45 +158,53 @@
           </packing>
         </child>
         <child>
-          <object class="GtkBox" id="box1">
+          <object class="GtkToggleButton" id="position_button">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="spacing">2</property>
-            <child>
-              <object class="GtkLabel" id="label_position">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label">--:--</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel" id="label3">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label">/</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="relief">none</property>
             <child>
-              <object class="GtkLabel" id="label_duration">
+              <object class="GtkBox" id="position_box">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="label" translatable="yes">--∶--</property>
+                <property name="spacing">2</property>
+                <child>
+                  <object class="GtkLabel" id="label_position">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label">--:--</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label">/</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label_duration">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label">--∶--</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
               </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">2</property>
-              </packing>
             </child>
           </object>
           <packing>
@@ -266,4 +273,137 @@ audio-volume-medium-symbolic</property>
       <class name="osd"/>
     </style>
   </object>
+  <object class="GtkPopover" id="position_popover">
+    <property name="can_focus">False</property>
+    <property name="relative_to">position_button</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkSeparator">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+            <property name="width">4</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_back_smaller_button">
+            <property name="label" translatable="yes" comments="minus 5 seconds">-5s</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go back 5 seconds</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_back_small_button">
+            <property name="label" translatable="yes" comments="minus 10 seconds">-10s</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go back 10 seconds</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_back_big_button">
+            <property name="label" translatable="yes" comments="minus 1 minute">-1m</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go back 1 minute</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_back_bigger_button">
+            <property name="label" translatable="yes" comments="minus 5 minutes">-5m</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go back 5 minutes</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">3</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_forward_smaller_button">
+            <property name="label" translatable="yes" comments="plus 5 seconds">+5s</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go forward 5 seconds</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_forward_small_button">
+            <property name="label" translatable="yes" comments="plus 10 seconds">+10s</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go forward 10 seconds</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_forward_big_button">
+            <property name="label" translatable="yes" comments="plus 1 minute">+1m</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go forward 1 minute</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="skip_forward_bigger_button">
+            <property name="label" translatable="yes" comments="plus 5 minutes">+5m</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Go forward 5 minutes</property>
+            <property name="relief">none</property>
+          </object>
+          <packing>
+            <property name="left_attach">3</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/extensions/gstreamer_tools/gth-media-viewer-page.c 
b/extensions/gstreamer_tools/gth-media-viewer-page.c
index 316476d6..43b60a07 100644
--- a/extensions/gstreamer_tools/gth-media-viewer-page.c
+++ b/extensions/gstreamer_tools/gth-media-viewer-page.c
@@ -92,6 +92,18 @@ static double default_rates[] = { 0.03, 0.06, 0.12, 0.25, 0.33, 0.50, 0.66, 1.0,
 static const GActionEntry actions[] = {
        { "video-screenshot", gth_browser_activate_video_screenshot },
        { "toggle-play", gth_browser_activate_toggle_play },
+       { "toggle-mute", gth_browser_activate_toggle_mute },
+       { "play-faster", gth_browser_activate_play_faster },
+       { "play-slower", gth_browser_activate_play_slower },
+       { "next-frame", gth_browser_activate_next_video_frame },
+       { "skip-forward-smaller", gth_browser_activate_skip_forward_smaller },
+       { "skip-forward-small", gth_browser_activate_skip_forward_small },
+       { "skip-forward-big", gth_browser_activate_skip_forward_big },
+       { "skip-forward-bigger", gth_browser_activate_skip_forward_bigger },
+       { "skip-back-smaller", gth_browser_activate_skip_back_smaller },
+       { "skip-back-small", gth_browser_activate_skip_back_small },
+       { "skip-back-big", gth_browser_activate_skip_back_big },
+       { "skip-back-bigger", gth_browser_activate_skip_back_bigger },
        { "video-zoom-fit", toggle_action_activated, NULL, "true", gth_browser_activate_video_zoom_fit },
 };
 
@@ -341,13 +353,13 @@ volume_value_changed_cb (GtkAdjustment *adjustment,
                return;
 
        /* cubic in [0,1], linear in [1,2] */
-       v = gtk_adjustment_get_value (adjustment);
+       v = gtk_adjustment_get_value (adjustment) / 100.0;
        if (v <= 1.0)
                v = (v * v * v);
 
-       g_object_set (self->priv->playbin,
-                     "volume", v,
-                     NULL);
+       g_object_set (self->priv->playbin, "volume", v, NULL);
+       if (v > 0)
+               g_object_set (self->priv->playbin, "mute", FALSE, NULL);
 }
 
 
@@ -358,15 +370,13 @@ static void position_value_changed_cb (GtkAdjustment *adjustment,
 static void
 update_current_position_bar (GthMediaViewerPage *self)
 {
-       GstFormat format;
-        gint64    current_value = 0;
+       gint64 current_value = 0;
 
-        format = GST_FORMAT_TIME;
-        if (gst_element_query_position (self->priv->playbin, format, &current_value)) {
-               char *s;
+       if (gst_element_query_position (self->priv->playbin, GST_FORMAT_TIME, &current_value)) {
+               char *s;
 
                if (self->priv->duration <= 0) {
-                       gst_element_query_duration (self->priv->playbin, format, &self->priv->duration);
+                       gst_element_query_duration (self->priv->playbin, GST_FORMAT_TIME, 
&self->priv->duration);
                        s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (self->priv->duration));
                        gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_duration")), s);
 
@@ -386,9 +396,8 @@ update_current_position_bar (GthMediaViewerPage *self)
 
                s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
                gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
-
                g_free (s);
-        }
+       }
 }
 
 
@@ -473,74 +482,60 @@ play_button_clicked_cb (GtkButton *button,
 }
 
 
-static int
-get_nearest_rate (double rate)
+static void
+play_slower_button_clicked_cb (GtkButton *button,
+                              gpointer   user_data)
 {
-       int    min_idx = -1;
-       double min_delta = 0;
-       int    i;
-
-       for (i = 0; i < G_N_ELEMENTS (default_rates); i++) {
-               double delta;
-
-               delta = fabs (default_rates[i] - rate);
-               if ((i == 0) || (delta < min_delta)) {
-                       min_delta = delta;
-                       min_idx = i;
-               }
-       }
-
-       return min_idx;
+       gth_media_viewer_page_play_slower (GTH_MEDIA_VIEWER_PAGE (user_data));
 }
 
 
 static void
-play_slower_button_clicked_cb (GtkButton *button,
+play_faster_button_clicked_cb (GtkButton *button,
                               gpointer   user_data)
 {
-       GthMediaViewerPage *self = user_data;
-       int                 i;
+       gth_media_viewer_page_play_faster (GTH_MEDIA_VIEWER_PAGE (user_data));
+}
 
-       i = get_nearest_rate (self->priv->rate);
-       if (i > 0)
-               self->priv->rate = default_rates[i - 1];
-       else
-               self->priv->rate = default_rates[0];
 
-       update_player_rate (self);
+static void
+loop_button_clicked_cb (GtkButton *button,
+                       gpointer   user_data)
+{
+       GthMediaViewerPage *self = user_data;
+       self->priv->loop = ! self->priv->loop;
 }
 
 
 static void
-play_faster_button_clicked_cb (GtkButton *button,
-                              gpointer   user_data)
+position_button_toggled_cb (GtkButton *button,
+                           gpointer   user_data)
 {
        GthMediaViewerPage *self = user_data;
-       int                 i;
 
-       i = get_nearest_rate (self->priv->rate);
-       if (i < G_N_ELEMENTS (default_rates) - 1)
-               self->priv->rate = default_rates[i + 1];
-       else
-               self->priv->rate = default_rates[G_N_ELEMENTS (default_rates) - 1];
-
-       update_player_rate (self);
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("position_button")))) {
+               gtk_popover_popup (GTK_POPOVER (GET_WIDGET ("position_popover")));
+               gth_browser_keep_mouse_visible (self->priv->browser, TRUE);
+       }
 }
 
 
 static void
-loop_button_clicked_cb (GtkButton *button,
-                       gpointer   user_data)
+position_popover_closed_cb (GtkPopover *popover,
+                           gpointer    user_data)
 {
        GthMediaViewerPage *self = user_data;
-       self->priv->loop = ! self->priv->loop;
+
+       gth_browser_keep_mouse_visible (self->priv->browser, FALSE);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("position_button")), FALSE);
 }
 
 
 static gboolean
 update_volume_from_playbin (GthMediaViewerPage *self)
 {
-       double volume, v;
+       double   volume, v;
+       gboolean mute;
 
        if (self->priv->update_volume_id != 0) {
                g_source_remove (self->priv->update_volume_id);
@@ -550,7 +545,9 @@ update_volume_from_playbin (GthMediaViewerPage *self)
        if ((self->priv->builder == NULL) || (self->priv->playbin == NULL))
                return FALSE;
 
-       g_object_get (self->priv->playbin, "volume", &volume, NULL);
+       g_object_get (self->priv->playbin, "volume", &volume, "mute", &mute, NULL);
+       if (mute)
+               volume = 0;
 
        /* cubic in [0,1], linear in [1,2] */
        if (volume <= 1.0)
@@ -559,7 +556,7 @@ update_volume_from_playbin (GthMediaViewerPage *self)
                v = volume;
 
        g_signal_handlers_block_by_func (GET_WIDGET ("volume_adjustment"), volume_value_changed_cb, self);
-       gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("volume_adjustment")), v);
+       gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("volume_adjustment")), v * 100.0);
        g_signal_handlers_unblock_by_func (GET_WIDGET ("volume_adjustment"), volume_value_changed_cb, self);
 
        return FALSE;
@@ -739,6 +736,11 @@ bus_message_cb (GstBus     *bus,
                return;
 
        switch (GST_MESSAGE_TYPE (message)) {
+       case GST_MESSAGE_ASYNC_DONE: {
+               update_current_position_bar (self);
+               break;
+       }
+
        case GST_MESSAGE_STATE_CHANGED: {
                GstState old_state;
                GstState new_state;
@@ -820,7 +822,7 @@ playbin_notify_volume_cb (GObject    *playbin,
        GthMediaViewerPage *self = user_data;
 
        if (self->priv->update_volume_id == 0)
-               self->priv->update_volume_id = g_idle_add ((GSourceFunc) update_volume_from_playbin, 
user_data);
+               self->priv->update_volume_id = g_idle_add ((GSourceFunc) update_volume_from_playbin, self);
 }
 
 
@@ -896,6 +898,7 @@ create_playbin (GthMediaViewerPage *self)
 
        g_object_set (self->priv->playbin,
                      "volume", (double) g_settings_get_int (self->priv->settings, 
PREF_GSTREAMER_TOOLS_VOLUME) / 100.0,
+                     "mute", g_settings_get_boolean (self->priv->settings, PREF_GSTREAMER_TOOLS_MUTE),
                      "force-aspect-ratio", TRUE,
                      NULL);
 
@@ -906,6 +909,10 @@ create_playbin (GthMediaViewerPage *self)
                          "notify::volume",
                          G_CALLBACK (playbin_notify_volume_cb),
                          self);
+       g_signal_connect (self->priv->playbin,
+                         "notify::mute",
+                         G_CALLBACK (playbin_notify_volume_cb),
+                         self);
        g_signal_connect (bus,
                          "message",
                          G_CALLBACK (bus_message_cb),
@@ -913,6 +920,69 @@ create_playbin (GthMediaViewerPage *self)
 }
 
 
+static void
+skip_back_bigger_button_clicked_cb (GtkButton *button,
+                                   gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), -60 * 5);
+}
+
+
+static void
+skip_back_big_button_clicked_cb (GtkButton *button,
+                                gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), -60);
+}
+
+
+static void
+skip_back_small_button_clicked_cb (GtkButton *button,
+                                  gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), -10);
+}
+
+
+static void
+skip_back_smaller_button_clicked_cb (GtkButton *button,
+                                    gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), -5);
+}
+
+
+static void
+skip_forward_smaller_button_clicked_cb (GtkButton *button,
+                                       gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), 5);
+}
+
+static void
+skip_forward_small_button_clicked_cb (GtkButton *button,
+                                     gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), 10);
+}
+
+
+static void
+skip_forward_big_button_clicked_cb (GtkButton *button,
+                                   gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), 60);
+}
+
+
+static void
+skip_forward_bigger_button_clicked_cb (GtkButton *button,
+                                      gpointer   user_data)
+{
+       gth_media_viewer_page_skip (GTH_MEDIA_VIEWER_PAGE (user_data), 60 * 5);
+}
+
+
 static void
 gth_media_viewer_page_real_activate (GthViewerPage *base,
                                     GthBrowser    *browser)
@@ -1004,6 +1074,46 @@ gth_media_viewer_page_real_activate (GthViewerPage *base,
                          "clicked",
                          G_CALLBACK (loop_button_clicked_cb),
                          self);
+       g_signal_connect (GET_WIDGET ("position_button"),
+                         "toggled",
+                         G_CALLBACK (position_button_toggled_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("position_popover"),
+                         "closed",
+                         G_CALLBACK (position_popover_closed_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_back_bigger_button"),
+                         "clicked",
+                         G_CALLBACK (skip_back_bigger_button_clicked_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_back_big_button"),
+                         "clicked",
+                         G_CALLBACK (skip_back_big_button_clicked_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_back_small_button"),
+                         "clicked",
+                         G_CALLBACK (skip_back_small_button_clicked_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_back_smaller_button"),
+                         "clicked",
+                         G_CALLBACK (skip_back_smaller_button_clicked_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_forward_smaller_button"),
+                         "clicked",
+                         G_CALLBACK (skip_forward_smaller_button_clicked_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_forward_small_button"),
+                         "clicked",
+                         G_CALLBACK (skip_forward_small_button_clicked_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_forward_big_button"),
+                         "clicked",
+                         G_CALLBACK (skip_forward_big_button_clicked_cb),
+                         self);
+       g_signal_connect (GET_WIDGET ("skip_forward_bigger_button"),
+                         "clicked",
+                         G_CALLBACK (skip_forward_bigger_button_clicked_cb),
+                         self);
 
        self->priv->mediabar_revealer = gtk_revealer_new ();
        gtk_revealer_set_transition_type (GTK_REVEALER (self->priv->mediabar_revealer), 
GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
@@ -1085,10 +1195,12 @@ gth_media_viewer_page_real_deactivate (GthViewerPage *base)
         }
 
        if (self->priv->playbin != NULL) {
-               double volume;
+               double   volume;
+               gboolean mute;
 
-               g_object_get (self->priv->playbin, "volume", &volume, NULL);
+               g_object_get (self->priv->playbin, "volume", &volume, "mute", &mute, NULL);
                g_settings_set_int (self->priv->settings, PREF_GSTREAMER_TOOLS_VOLUME, (int) (volume * 
100.0));
+               g_settings_set_boolean (self->priv->settings, PREF_GSTREAMER_TOOLS_MUTE, mute);
 
                g_settings_set_boolean (self->priv->settings, PREF_GSTREAMER_ZOOM_TO_FIT, 
self->priv->fit_if_larger);
 
@@ -1475,6 +1587,13 @@ gth_media_viewer_page_get_file_data (GthMediaViewerPage *self)
 }
 
 
+static gint64
+_gth_media_viewer_page_get_current_time (GthMediaViewerPage *self)
+{
+       return (gint64) (gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET ("position_adjustment"))) / 
100.0 * self->priv->duration);
+}
+
+
 void
 gth_media_viewer_page_toggle_play (GthMediaViewerPage *self)
 {
@@ -1494,15 +1613,12 @@ gth_media_viewer_page_toggle_play (GthMediaViewerPage *self)
                                          0.0);
                }
                else {
-                       gint64 current_value;
-
-                       current_value = (gint64) (gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET 
("position_adjustment"))) / 100.0 * self->priv->duration);
                        gst_element_seek (self->priv->playbin,
                                          self->priv->rate,
                                          GST_FORMAT_TIME,
                                          GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
                                          GST_SEEK_TYPE_SET,
-                                         current_value,
+                                         _gth_media_viewer_page_get_current_time (self),
                                          GST_SEEK_TYPE_NONE,
                                          0.0);
                }
@@ -1528,3 +1644,113 @@ gth_media_viewer_page_set_fit_if_larger (GthMediaViewerPage *self,
                gth_window_change_action_state (GTH_WINDOW (self->priv->browser), "video-zoom-fit", 
self->priv->fit_if_larger);
        }
 }
+
+
+void
+gth_media_viewer_page_skip (GthMediaViewerPage *self,
+                           int                 seconds)
+{
+       GstSeekFlags seek_flags;
+       GstSeekType  start_type;
+       gint64       start;
+
+       if (self->priv->playbin == NULL)
+               return;
+
+       seek_flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE;
+       start_type = GST_SEEK_TYPE_SET;
+       start = _gth_media_viewer_page_get_current_time (self) + (seconds * GST_SECOND);
+       if (start < 0)
+               start = 0;
+       if (start >= self->priv->duration) {
+               seek_flags |= GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_SNAP_BEFORE | GST_SEEK_FLAG_TRICKMODE;
+               start_type = GST_SEEK_TYPE_END;
+               start = 0;
+       }
+
+       gst_element_seek (self->priv->playbin,
+                         self->priv->rate,
+                         GST_FORMAT_TIME,
+                         seek_flags,
+                         start_type,
+                         start,
+                         GST_SEEK_TYPE_NONE,
+                         0.0);
+}
+
+
+void
+gth_media_viewer_page_next_frame (GthMediaViewerPage *self)
+{
+       if (self->priv->playbin == NULL)
+               return;
+
+       if (! self->priv->has_video)
+               return;
+
+       gst_element_send_event (self->priv->playbin, gst_event_new_step (GST_FORMAT_BUFFERS, 1, ABS 
(self->priv->rate), TRUE, FALSE));
+}
+
+
+void
+gth_media_viewer_page_toggle_mute (GthMediaViewerPage *self)
+{
+       gboolean mute;
+
+       if (self->priv->playbin == NULL)
+               return;
+
+       g_object_get (self->priv->playbin, "mute", &mute, NULL);
+       g_object_set (self->priv->playbin, "mute", ! mute, NULL);
+}
+
+
+static int
+get_nearest_rate (double rate)
+{
+       int    min_idx = -1;
+       double min_delta = 0;
+       int    i;
+
+       for (i = 0; i < G_N_ELEMENTS (default_rates); i++) {
+               double delta;
+
+               delta = fabs (default_rates[i] - rate);
+               if ((i == 0) || (delta < min_delta)) {
+                       min_delta = delta;
+                       min_idx = i;
+               }
+       }
+
+       return min_idx;
+}
+
+
+void
+gth_media_viewer_page_play_faster (GthMediaViewerPage *self)
+{
+       int i;
+
+       i = get_nearest_rate (self->priv->rate);
+       if (i < G_N_ELEMENTS (default_rates) - 1)
+               self->priv->rate = default_rates[i + 1];
+       else
+               self->priv->rate = default_rates[G_N_ELEMENTS (default_rates) - 1];
+
+       update_player_rate (self);
+}
+
+
+void
+gth_media_viewer_page_play_slower (GthMediaViewerPage *self)
+{
+       int i;
+
+       i = get_nearest_rate (self->priv->rate);
+       if (i > 0)
+               self->priv->rate = default_rates[i - 1];
+       else
+               self->priv->rate = default_rates[0];
+
+       update_player_rate (self);
+}
diff --git a/extensions/gstreamer_tools/gth-media-viewer-page.h 
b/extensions/gstreamer_tools/gth-media-viewer-page.h
index 9b8e6c4d..6c8b2b32 100644
--- a/extensions/gstreamer_tools/gth-media-viewer-page.h
+++ b/extensions/gstreamer_tools/gth-media-viewer-page.h
@@ -59,6 +59,12 @@ void           gth_media_viewer_page_toggle_play    (GthMediaViewerPage *self);
 void           gth_media_viewer_page_set_fit_if_larger
                                                    (GthMediaViewerPage *self,
                                                     gboolean            fit_if_larger);
+void           gth_media_viewer_page_skip          (GthMediaViewerPage *self,
+                                                    int                 seconds);
+void           gth_media_viewer_page_next_frame            (GthMediaViewerPage *self);
+void           gth_media_viewer_page_toggle_mute    (GthMediaViewerPage *self);
+void           gth_media_viewer_page_play_faster    (GthMediaViewerPage *self);
+void           gth_media_viewer_page_play_slower    (GthMediaViewerPage *self);
 
 G_END_DECLS
 
diff --git a/extensions/gstreamer_tools/preferences.h b/extensions/gstreamer_tools/preferences.h
index 68b517c2..86f39a8c 100644
--- a/extensions/gstreamer_tools/preferences.h
+++ b/extensions/gstreamer_tools/preferences.h
@@ -30,6 +30,7 @@
 
 #define  PREF_GSTREAMER_TOOLS_SCREESHOT_LOCATION "screenshot-location"
 #define  PREF_GSTREAMER_TOOLS_VOLUME             "volume"
+#define  PREF_GSTREAMER_TOOLS_MUTE               "mute"
 #define  PREF_GSTREAMER_USE_HARDWARE_ACCEL       "use-hardware-acceleration"
 #define  PREF_GSTREAMER_ZOOM_TO_FIT              "zoom-to-fit"
 


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