bacon-volume.c
上传用户:ledjyj
上传日期:2014-08-27
资源大小:2639k
文件大小:17k
源码类别:

驱动编程

开发平台:

Unix_Linux

  1. /* Volume Button / popup widget
  2.  * (c) copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
  3.  *
  4.  * This library is free software; you can redistribute it and/or
  5.  * modify it under the terms of the GNU Library General Public
  6.  * License as published by the Free Software Foundation; either
  7.  * version 2 of the License, or (at your option) any later version.
  8.  *
  9.  * This library is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12.  * Library General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU Library General Public
  15.  * License along with this library; if not, write to the
  16.  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  17.  * Boston, MA 02111-1307, USA.
  18.  */
  19. #ifdef HAVE_CONFIG_H
  20. #include "config.h"
  21. #endif
  22. #define _GNU_SOURCE
  23. #include <math.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <glib/gi18n.h>
  27. #include <gtk/gtk.h>
  28. #include "bacon-volume.h"
  29. #define SCALE_SIZE 100
  30. #define CLICK_TIMEOUT 250
  31. enum {
  32.   SIGNAL_VALUE_CHANGED,
  33.   NUM_SIGNALS
  34. };
  35. static void bacon_volume_button_class_init (BaconVolumeButtonClass * klass);
  36. static void bacon_volume_button_init (BaconVolumeButton * button);
  37. static void bacon_volume_button_dispose (GObject        * object);
  38. static gboolean bacon_volume_button_scroll (GtkWidget      * widget,
  39.  GdkEventScroll * event);
  40. static gboolean bacon_volume_button_press (GtkWidget      * widget,
  41.  GdkEventButton * event);
  42. static gboolean cb_dock_press (GtkWidget      * widget,
  43.  GdkEventButton * event,
  44.  gpointer         data);
  45. static gboolean cb_button_press (GtkWidget      * widget,
  46.  GdkEventButton * event,
  47.  gpointer         data);
  48. static gboolean cb_button_release (GtkWidget      * widget,
  49.  GdkEventButton * event,
  50.  gpointer         data);
  51. static void bacon_volume_scale_value_changed(GtkRange       * range);
  52. /* see below for scale definitions */
  53. static GtkWidget *bacon_volume_scale_new (BaconVolumeButton * button,
  54.  float min, float max,
  55.  float step);
  56. static GtkButtonClass *parent_class = NULL;
  57. static guint signals[NUM_SIGNALS] = { 0 };
  58. GType
  59. bacon_volume_button_get_type (void)
  60. {
  61.   static GType bacon_volume_button_type = 0;
  62.   if (!bacon_volume_button_type) {
  63.     static const GTypeInfo bacon_volume_button_info = {
  64.       sizeof (BaconVolumeButtonClass),
  65.       NULL,
  66.       NULL,
  67.       (GClassInitFunc) bacon_volume_button_class_init,
  68.       NULL,
  69.       NULL,
  70.       sizeof (BaconVolumeButton),
  71.       0,
  72.       (GInstanceInitFunc) bacon_volume_button_init,
  73.       NULL
  74.     };
  75.     bacon_volume_button_type =
  76. g_type_register_static (GTK_TYPE_BUTTON, 
  77. "BaconVolumeButton",
  78. &bacon_volume_button_info, 0);
  79.   }
  80.   return bacon_volume_button_type;
  81. }
  82. static void
  83. bacon_volume_button_class_init (BaconVolumeButtonClass *klass)
  84. {
  85.   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  86.   GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
  87.   parent_class = g_type_class_ref (GTK_TYPE_BUTTON);
  88.   /* events */
  89.   gobject_class->dispose = bacon_volume_button_dispose;
  90.   gtkwidget_class->button_press_event = bacon_volume_button_press;
  91.   gtkwidget_class->scroll_event = bacon_volume_button_scroll;
  92.   /* signals */
  93.   signals[SIGNAL_VALUE_CHANGED] = g_signal_new ("value-changed",
  94.       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
  95.       G_STRUCT_OFFSET (BaconVolumeButtonClass, value_changed),
  96.       NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
  97. }
  98. static void
  99. bacon_volume_button_init (BaconVolumeButton *button)
  100. {
  101.   button->timeout = FALSE;
  102.   button->click_id = 0;
  103.   button->dock = button->scale = NULL;
  104. #ifndef HAVE_GTK_ONLY
  105.   button->theme = gtk_icon_theme_get_default ();
  106. #endif
  107. }
  108. static void
  109. bacon_volume_button_dispose (GObject *object)
  110. {
  111.   BaconVolumeButton *button = BACON_VOLUME_BUTTON (object);
  112.   if (button->dock) {
  113.     gtk_widget_destroy (button->dock);
  114.     button->dock = NULL;
  115.   }
  116.   if (button->theme) {
  117.     g_object_unref (G_OBJECT (button->theme));
  118.     button->theme = NULL;
  119.   }
  120.   if (button->click_id != 0) {
  121.     g_source_remove (button->click_id);
  122.     button->click_id = 0;
  123.   }
  124.   G_OBJECT_CLASS (parent_class)->dispose (object);
  125. }
  126. /*
  127.  * public API.
  128.  */
  129. GtkWidget *
  130. bacon_volume_button_new (GtkIconSize size,
  131.  float min, float max,
  132.  float step)
  133. {
  134.   BaconVolumeButton *button;
  135.   GtkWidget *frame, *box;
  136.   button = g_object_new (BACON_TYPE_VOLUME_BUTTON, NULL);
  137.   button->size = size;
  138.   gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
  139. #ifndef HAVE_GTK_ONLY
  140.   /* image */
  141.   button->image = gtk_image_new ();
  142.   gtk_container_add (GTK_CONTAINER (button), button->image);
  143.   gtk_widget_show_all (button->image);
  144. #endif
  145.   /* window */
  146.   button->dock = gtk_window_new (GTK_WINDOW_POPUP);
  147.   g_signal_connect (button->dock, "button-press-event",
  148.     G_CALLBACK (cb_dock_press), button);
  149.   gtk_window_set_decorated (GTK_WINDOW (button->dock), FALSE);
  150.   /* frame */
  151.   frame = gtk_frame_new (NULL);
  152.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
  153.   gtk_container_add (GTK_CONTAINER (button->dock), frame);
  154.   box = gtk_vbox_new (FALSE, 0);
  155.   gtk_container_add (GTK_CONTAINER (frame), box);
  156.   /* + */
  157.   button->plus = gtk_button_new_with_label (_("+"));
  158.   gtk_button_set_relief (GTK_BUTTON (button->plus), GTK_RELIEF_NONE);
  159.   g_signal_connect (button->plus, "button-press-event",
  160.     G_CALLBACK (cb_button_press), button);
  161.   g_signal_connect (button->plus, "button-release-event",
  162.     G_CALLBACK (cb_button_release), button);
  163.   gtk_box_pack_start (GTK_BOX (box), button->plus, TRUE, FALSE, 0);
  164.   /* scale */
  165.   button->scale = bacon_volume_scale_new (button, min, max, step);
  166.   gtk_widget_set_size_request (button->scale, -1, SCALE_SIZE);
  167.   gtk_scale_set_draw_value (GTK_SCALE (button->scale), FALSE);
  168.   gtk_range_set_inverted (GTK_RANGE (button->scale), TRUE);
  169.   gtk_box_pack_start (GTK_BOX (box), button->scale, TRUE, FALSE, 0);
  170.   /* - */
  171.   button->min = gtk_button_new_with_label (_("-"));
  172.   gtk_button_set_relief (GTK_BUTTON (button->min), GTK_RELIEF_NONE);
  173.   g_signal_connect (button->min, "button-press-event",
  174.    G_CALLBACK (cb_button_press), button);
  175.   g_signal_connect (button->min, "button-release-event",
  176.     G_CALLBACK (cb_button_release), button);
  177.   gtk_box_pack_start (GTK_BOX (box), button->min, TRUE, FALSE, 0);
  178.   /* call callback once so original icon is drawn */
  179.   bacon_volume_scale_value_changed (GTK_RANGE (button->scale));
  180.   return GTK_WIDGET (button);
  181. }
  182. float
  183. bacon_volume_button_get_value (BaconVolumeButton * button)
  184. {
  185.   g_return_val_if_fail (button != NULL, 0);
  186.   return gtk_range_get_value (GTK_RANGE (button->scale));
  187. }
  188. void
  189. bacon_volume_button_set_value (BaconVolumeButton * button,
  190.        float value)
  191. {
  192.   g_return_if_fail (button != NULL);
  193.   gtk_range_set_value (GTK_RANGE (button->scale), value);
  194. }
  195. /*
  196.  * button callbacks.
  197.  */
  198. static gboolean
  199. bacon_volume_button_scroll (GtkWidget      * widget,
  200.     GdkEventScroll * event)
  201. {
  202.   BaconVolumeButton *button = BACON_VOLUME_BUTTON (widget);
  203.   GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
  204.   float d;
  205.   if (event->type != GDK_SCROLL)
  206.     return FALSE;
  207.   d = bacon_volume_button_get_value (button);
  208.   if (event->direction == GDK_SCROLL_UP) {
  209.     d += adj->step_increment;
  210.     if (d > adj->upper)
  211.       d = adj->upper;
  212.   } else {
  213.     d -= adj->step_increment;
  214.     if (d < adj->lower)
  215.       d = adj->lower;
  216.   }
  217.   bacon_volume_button_set_value (button, d);
  218.   return TRUE;
  219. }
  220. static gboolean
  221. bacon_volume_button_press (GtkWidget      * widget,
  222.    GdkEventButton * event)
  223. {
  224.   BaconVolumeButton *button = BACON_VOLUME_BUTTON (widget);
  225.   GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
  226.   gint x, y, m, dx, dy, sx, sy, ystartoff, mouse_y;
  227.   float v;
  228.   GdkEventButton *e;
  229.   /* position roughly */
  230.   gdk_window_get_origin (widget->window, &x, &y);
  231.   x += widget->allocation.x;
  232.   y += widget->allocation.y;
  233.   gtk_window_move (GTK_WINDOW (button->dock), x, y - (SCALE_SIZE / 2));
  234.   gtk_widget_show_all (button->dock);
  235.   gdk_window_get_origin (button->dock->window, &dx, &dy);
  236.   dy += button->dock->allocation.y;
  237.   gdk_window_get_origin (button->scale->window, &sx, &sy);
  238.   sy += button->scale->allocation.y;
  239.   ystartoff = sy - dy;
  240.   mouse_y = event->y;
  241.   button->timeout = TRUE;
  242.   /* position (needs widget to be shown already) */
  243.   v = bacon_volume_button_get_value (button) / (adj->upper - adj->lower);
  244.   x += (widget->allocation.width - button->dock->allocation.width) / 2;
  245.   y -= ystartoff;
  246.   y -= GTK_RANGE (button->scale)->min_slider_size / 2;
  247.   m = button->scale->allocation.height -
  248.       GTK_RANGE (button->scale)->min_slider_size;
  249.   y -= m * (1.0 - v);
  250.   y += mouse_y;
  251.   gtk_window_move (GTK_WINDOW (button->dock), x, y);
  252.   gdk_window_get_origin (button->scale->window, &sx, &sy);
  253.   GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
  254.   /* grab focus */
  255.   gtk_widget_grab_focus (button->dock);
  256.   gtk_grab_add (button->dock);
  257.   gdk_pointer_grab (button->dock->window, TRUE,
  258.       GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
  259.       GDK_POINTER_MOTION_MASK, NULL, NULL, GDK_CURRENT_TIME);
  260.   gdk_keyboard_grab (button->dock->window, TRUE, GDK_CURRENT_TIME);
  261.   /* forward event to the slider */
  262.   e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
  263.   e->window = button->scale->window;
  264.   /* position: the X position isn't relevant, halfway will work just fine.
  265.    * The vertical position should be *exactly* in the middle of the slider
  266.    * of the scale; if we don't do that correctly, it'll move from its current
  267.    * position, which means a position change on-click, which is bad. */
  268.   e->x = button->scale->allocation.width / 2;
  269.   m = button->scale->allocation.height -
  270.       GTK_RANGE (button->scale)->min_slider_size;
  271.   e->y = ((1.0 - v) * m) + GTK_RANGE (button->scale)->min_slider_size / 2;
  272.   gtk_widget_event (button->scale, (GdkEvent *) e);
  273.   e->window = event->window;
  274.   gdk_event_free ((GdkEvent *) e);
  275.   button->pop_time = event->time;
  276.   return TRUE;
  277. }
  278. /*
  279.  * +/- button callbacks.
  280.  */
  281. static gboolean
  282. cb_button_timeout (gpointer data)
  283. {
  284.   BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
  285.   GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
  286.   float val;
  287.   gboolean res = TRUE;
  288.   if (button->click_id == 0)
  289.     return FALSE;
  290.   val = bacon_volume_button_get_value (button);
  291.   val += button->direction;
  292.   if (val <= adj->lower) {
  293.     res = FALSE;
  294.     val = adj->lower;
  295.   } else if (val > adj->upper) {
  296.     res = FALSE;
  297.     val = adj->upper;
  298.   }
  299.   bacon_volume_button_set_value (button, val);
  300.   if (!res) {
  301.     g_source_remove (button->click_id);
  302.     button->click_id = 0;
  303.   }
  304.   return res;
  305. }
  306. static gboolean
  307. cb_button_press (GtkWidget      * widget,
  308.  GdkEventButton * event,
  309.  gpointer         data)
  310. {
  311.   BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
  312.   GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
  313.   if (button->click_id != 0)
  314.     g_source_remove (button->click_id);
  315.   button->direction = (widget == button->plus) ?
  316.       fabs (adj->page_increment) : - fabs (adj->page_increment);
  317.   button->click_id = g_timeout_add (CLICK_TIMEOUT,
  318.     (GSourceFunc) cb_button_timeout, button);
  319.   cb_button_timeout (button);
  320.   return TRUE;
  321. }
  322. static gboolean
  323. cb_button_release (GtkWidget      * widget,
  324.    GdkEventButton * event,
  325.    gpointer         data)
  326. {
  327.   BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
  328.   if (button->click_id != 0) {
  329.     g_source_remove (button->click_id);
  330.     button->click_id = 0;
  331.   }
  332.   return TRUE;
  333. }
  334. /*
  335.  * Scale callbacks.
  336.  */
  337. static void
  338. bacon_volume_release_grab (BaconVolumeButton *button,
  339.    GdkEventButton * event)
  340. {
  341.   GdkEventButton *e;
  342.   /* ungrab focus */
  343.   gdk_keyboard_ungrab (GDK_CURRENT_TIME);
  344.   gdk_pointer_ungrab (GDK_CURRENT_TIME);
  345.   gtk_grab_remove (button->dock);
  346.   /* hide again */
  347.   gtk_widget_hide (button->dock);
  348.   button->timeout = FALSE;
  349.   e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
  350.   e->window = GTK_WIDGET (button)->window;
  351.   e->type = GDK_BUTTON_RELEASE;
  352.   gtk_widget_event (GTK_WIDGET (button), (GdkEvent *) e);
  353.   e->window = event->window;
  354.   gdk_event_free ((GdkEvent *) e);
  355. }
  356. static gboolean
  357. cb_dock_press (GtkWidget      * widget,
  358.        GdkEventButton * event,
  359.        gpointer         data)
  360. {
  361.   //GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *) event);
  362.   BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
  363.   if (/*ewidget == button->dock &&*/ event->type == GDK_BUTTON_PRESS) {
  364.     bacon_volume_release_grab (button, event);
  365.     return TRUE;
  366.   }
  367.   return FALSE;
  368. }
  369. /*
  370.  * Scale stuff.
  371.  */
  372. #define BACON_TYPE_VOLUME_SCALE 
  373.   (bacon_volume_scale_get_type ())
  374. #define BACON_VOLUME_SCALE(obj) 
  375.   (G_TYPE_CHECK_INSTANCE_CAST ((obj), BACON_TYPE_VOLUME_SCALE, 
  376.        BaconVolumeScale))
  377. typedef struct _BaconVolumeScale {
  378.   GtkVScale parent;
  379.   BaconVolumeButton *button;
  380. } BaconVolumeScale;
  381. static GType bacon_volume_scale_get_type  (void);
  382. static void bacon_volume_scale_class_init    (GtkVScaleClass * klass);
  383. static gboolean bacon_volume_scale_press  (GtkWidget      * widget,
  384.   GdkEventButton * event);
  385. static gboolean bacon_volume_scale_release  (GtkWidget      * widget,
  386.   GdkEventButton * event);
  387. static GtkVScaleClass *scale_parent_class = NULL;
  388. static GType
  389. bacon_volume_scale_get_type (void)
  390. {
  391.   static GType bacon_volume_scale_type = 0;
  392.   if (!bacon_volume_scale_type) {
  393.     static const GTypeInfo bacon_volume_scale_info = {
  394.       sizeof (GtkVScaleClass),
  395.       NULL,
  396.       NULL,
  397.       (GClassInitFunc) bacon_volume_scale_class_init,
  398.       NULL,
  399.       NULL,
  400.       sizeof (BaconVolumeScale),
  401.       0,
  402.       NULL,
  403.       NULL
  404.     };
  405.     bacon_volume_scale_type =
  406.         g_type_register_static (GTK_TYPE_VSCALE,
  407. "BaconVolumeScale",
  408. &bacon_volume_scale_info, 0);
  409.   }
  410.   return bacon_volume_scale_type;
  411. }
  412. static void
  413. bacon_volume_scale_class_init (GtkVScaleClass * klass)
  414. {
  415.   GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
  416.   GtkRangeClass *gtkrange_class = GTK_RANGE_CLASS (klass);
  417.   scale_parent_class = g_type_class_ref (GTK_TYPE_VSCALE);
  418.   gtkwidget_class->button_press_event = bacon_volume_scale_press;
  419.   gtkwidget_class->button_release_event = bacon_volume_scale_release;
  420.   gtkrange_class->value_changed = bacon_volume_scale_value_changed;
  421. }
  422. static GtkWidget *
  423. bacon_volume_scale_new (BaconVolumeButton * button,
  424. float min, float max,
  425. float step)
  426. {
  427.   BaconVolumeScale *scale = g_object_new (BACON_TYPE_VOLUME_SCALE, NULL);
  428.   GtkObject *adj;
  429.   adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
  430.   gtk_range_set_adjustment (GTK_RANGE (scale), GTK_ADJUSTMENT (adj));
  431.   scale->button = button;
  432.   return GTK_WIDGET (scale);
  433. }
  434. static gboolean
  435. bacon_volume_scale_press (GtkWidget      * widget,
  436.   GdkEventButton * event)
  437. {
  438.   BaconVolumeScale *scale = BACON_VOLUME_SCALE (widget);
  439.   BaconVolumeButton *button = scale->button;
  440.   /* the scale will grab input; if we have input grabbed, all goes
  441.    * horribly wrong, so let's not do that. */
  442.   gtk_grab_remove (button->dock);
  443.   return GTK_WIDGET_CLASS (scale_parent_class)->button_press_event (widget, event);
  444. }
  445. static gboolean
  446. bacon_volume_scale_release (GtkWidget      * widget,
  447.     GdkEventButton * event)
  448. {
  449.   BaconVolumeScale *scale = BACON_VOLUME_SCALE (widget);
  450.   BaconVolumeButton *button = scale->button;
  451.   gboolean res;
  452.   if (button->timeout) {
  453.     /* if we did a quick click, leave the window open; else, hide it */
  454.     if (event->time > button->pop_time + CLICK_TIMEOUT) {
  455.       bacon_volume_release_grab (button, event);
  456.       GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
  457.       return TRUE;
  458.     }
  459.     button->timeout = FALSE;
  460.   }
  461.   res = GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
  462.   /* the scale will release input; right after that, we *have to* grab
  463.    * it back so we can catch out-of-scale clicks and hide the popup,
  464.    * so I basically want a g_signal_connect_after_always(), but I can't
  465.    * find that, so we do this complex 'first-call-parent-then-do-actual-
  466.    * action' thingy... */
  467.   gtk_grab_add (button->dock);
  468.   return res;
  469. }
  470. static void
  471. bacon_volume_scale_value_changed (GtkRange * range)
  472. {
  473.   BaconVolumeScale *scale = BACON_VOLUME_SCALE (range);
  474.   BaconVolumeButton *button = scale->button;
  475.   GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
  476.   float step = (adj->upper - adj->lower) / 4;
  477.   float val = gtk_range_get_value (range);
  478.   gint w, h;
  479. #ifdef HAVE_GTK_ONLY
  480.   char *s;
  481.   /* update label */
  482.   s = g_strdup_printf ("%d", lrintf (val));
  483.   gtk_button_set_label (GTK_BUTTON (button), s);
  484.   g_free (s);
  485. #else
  486.   const char *s;
  487.   GdkPixbuf *buf;
  488.   if (val == adj->lower)
  489.     s = "stock_volume-mute";
  490.   else if (val > adj->lower && val <= adj->lower + step)
  491.     s = "stock_volume-0";
  492.   else if (val > adj->lower + step && val <= adj->lower + step * 2)
  493.     s = "stock_volume-min";
  494.   else if (val > adj->lower + step * 2 && val <= adj->lower + step * 3)
  495.     s = "stock_volume-med";
  496.   else
  497.     s = "stock_volume-max";
  498.   /* update image */
  499.   gtk_icon_size_lookup (button->size, &w, &h);
  500.   buf = gtk_icon_theme_load_icon (button->theme, s, w, 0, NULL);
  501.   gtk_image_set_from_pixbuf (GTK_IMAGE (button->image), buf);
  502. #endif
  503.   /* signal */
  504.   g_signal_emit (button, signals[SIGNAL_VALUE_CHANGED], 0);
  505. }