You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

554 lines
20 KiB

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  2. *
  3. * Copyright (C) 2008 William Jon McCann
  4. * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  19. *
  20. */
  21. #include <glib.h>
  22. #include <glib/gi18n.h>
  23. #include <glib-object.h>
  24. #include <gtk/gtk.h>
  25. #include <canberra-gtk.h>
  26. #include <libmatemixer/matemixer.h>
  27. #include "gvc-balance-bar.h"
  28. #define BALANCE_BAR_STYLE \
  29. "style \"balance-bar-scale-style\" {\n" \
  30. " GtkScale::trough-side-details = 0\n" \
  31. "}\n" \
  32. "widget \"*.balance-bar-scale\" style : rc \"balance-bar-scale-style\"\n"
  33. #define SCALE_SIZE 128
  34. #define GVC_BALANCE_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBarPrivate))
  35. struct _GvcBalanceBarPrivate
  36. {
  37. GvcBalanceType btype;
  38. GtkWidget *scale_box;
  39. GtkWidget *start_box;
  40. GtkWidget *end_box;
  41. GtkWidget *label;
  42. GtkWidget *scale;
  43. GtkAdjustment *adjustment;
  44. GtkSizeGroup *size_group;
  45. gboolean symmetric;
  46. MateMixerStreamControl *control;
  47. gint lfe_channel;
  48. };
  49. enum
  50. {
  51. PROP_0,
  52. PROP_CONTROL,
  53. PROP_BALANCE_TYPE,
  54. N_PROPERTIES
  55. };
  56. static GParamSpec *properties[N_PROPERTIES] = { NULL, };
  57. static void gvc_balance_bar_class_init (GvcBalanceBarClass *klass);
  58. static void gvc_balance_bar_init (GvcBalanceBar *balance_bar);
  59. static void gvc_balance_bar_dispose (GObject *object);
  60. static gboolean on_scale_scroll_event (GtkWidget *widget,
  61. GdkEventScroll *event,
  62. GvcBalanceBar *bar);
  63. static void on_adjustment_value_changed (GtkAdjustment *adjustment,
  64. GvcBalanceBar *bar);
  65. G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_BOX)
  66. static void
  67. create_scale_box (GvcBalanceBar *bar)
  68. {
  69. #if GTK_CHECK_VERSION (3, 0, 0)
  70. bar->priv->scale_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  71. bar->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  72. bar->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  73. bar->priv->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL,
  74. bar->priv->adjustment);
  75. #if GTK_CHECK_VERSION (3, 4, 0)
  76. /* Balance and fade scales do not have an origin */
  77. if (bar->priv->btype != BALANCE_TYPE_LFE)
  78. gtk_scale_set_has_origin (GTK_SCALE (bar->priv->scale), FALSE);
  79. #endif
  80. #else
  81. bar->priv->scale_box = gtk_hbox_new (FALSE, 6);
  82. bar->priv->start_box = gtk_hbox_new (FALSE, 6);
  83. bar->priv->end_box = gtk_hbox_new (FALSE, 6);
  84. bar->priv->scale = gtk_hscale_new (bar->priv->adjustment);
  85. /* GTK2 way to remove the origin */
  86. if (bar->priv->btype != BALANCE_TYPE_LFE) {
  87. gtk_rc_parse_string (BALANCE_BAR_STYLE);
  88. gtk_widget_set_name (bar->priv->scale, "balance-bar-scale");
  89. }
  90. #endif
  91. gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1);
  92. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  93. bar->priv->start_box,
  94. FALSE, FALSE, 0);
  95. gtk_box_pack_start (GTK_BOX (bar->priv->start_box),
  96. bar->priv->label,
  97. FALSE, FALSE, 0);
  98. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  99. bar->priv->scale,
  100. TRUE, TRUE, 0);
  101. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  102. bar->priv->end_box,
  103. FALSE, FALSE, 0);
  104. ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE);
  105. gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK);
  106. g_signal_connect (G_OBJECT (bar->priv->scale),
  107. "scroll-event",
  108. G_CALLBACK (on_scale_scroll_event),
  109. bar);
  110. if (bar->priv->size_group != NULL) {
  111. gtk_size_group_add_widget (bar->priv->size_group,
  112. bar->priv->start_box);
  113. if (bar->priv->symmetric)
  114. gtk_size_group_add_widget (bar->priv->size_group,
  115. bar->priv->end_box);
  116. }
  117. gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE);
  118. }
  119. static void
  120. update_scale_marks (GvcBalanceBar *bar)
  121. {
  122. gchar *str_lower = NULL,
  123. *str_upper = NULL;
  124. gdouble lower,
  125. upper;
  126. gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale));
  127. switch (bar->priv->btype) {
  128. case BALANCE_TYPE_RL:
  129. str_lower = g_strdup_printf ("<small>%s</small>", C_("balance", "Left"));
  130. str_upper = g_strdup_printf ("<small>%s</small>", C_("balance", "Right"));
  131. break;
  132. case BALANCE_TYPE_FR:
  133. str_lower = g_strdup_printf ("<small>%s</small>", C_("balance", "Rear"));
  134. str_upper = g_strdup_printf ("<small>%s</small>", C_("balance", "Front"));
  135. break;
  136. case BALANCE_TYPE_LFE:
  137. str_lower = g_strdup_printf ("<small>%s</small>", C_("balance", "Minimum"));
  138. str_upper = g_strdup_printf ("<small>%s</small>", C_("balance", "Maximum"));
  139. break;
  140. }
  141. lower = gtk_adjustment_get_lower (bar->priv->adjustment);
  142. gtk_scale_add_mark (GTK_SCALE (bar->priv->scale),
  143. lower,
  144. GTK_POS_BOTTOM,
  145. str_lower);
  146. upper = gtk_adjustment_get_upper (bar->priv->adjustment);
  147. gtk_scale_add_mark (GTK_SCALE (bar->priv->scale),
  148. upper,
  149. GTK_POS_BOTTOM,
  150. str_upper);
  151. g_free (str_lower);
  152. g_free (str_upper);
  153. if (bar->priv->btype != BALANCE_TYPE_LFE)
  154. gtk_scale_add_mark (GTK_SCALE (bar->priv->scale),
  155. (upper - lower) / 2 + lower,
  156. GTK_POS_BOTTOM,
  157. NULL);
  158. }
  159. void
  160. gvc_balance_bar_set_size_group (GvcBalanceBar *bar,
  161. GtkSizeGroup *group,
  162. gboolean symmetric)
  163. {
  164. g_return_if_fail (GVC_IS_BALANCE_BAR (bar));
  165. g_return_if_fail (GTK_IS_SIZE_GROUP (group));
  166. bar->priv->size_group = group;
  167. bar->priv->symmetric = symmetric;
  168. if (bar->priv->size_group != NULL) {
  169. gtk_size_group_add_widget (bar->priv->size_group,
  170. bar->priv->start_box);
  171. if (bar->priv->symmetric)
  172. gtk_size_group_add_widget (bar->priv->size_group,
  173. bar->priv->end_box);
  174. }
  175. gtk_widget_queue_draw (GTK_WIDGET (bar));
  176. }
  177. static void
  178. update_balance_value (GvcBalanceBar *bar)
  179. {
  180. gdouble value = 0;
  181. switch (bar->priv->btype) {
  182. case BALANCE_TYPE_RL:
  183. value = mate_mixer_stream_control_get_balance (bar->priv->control);
  184. g_debug ("Balance value changed to %.2f", value);
  185. break;
  186. case BALANCE_TYPE_FR:
  187. value = mate_mixer_stream_control_get_fade (bar->priv->control);
  188. g_debug ("Fade value changed to %.2f", value);
  189. break;
  190. case BALANCE_TYPE_LFE:
  191. value = mate_mixer_stream_control_get_channel_volume (bar->priv->control,
  192. bar->priv->lfe_channel);
  193. g_debug ("Subwoofer volume changed to %.0f", value);
  194. break;
  195. }
  196. gtk_adjustment_set_value (bar->priv->adjustment, value);
  197. }
  198. static void
  199. on_balance_value_changed (MateMixerStream *stream,
  200. GParamSpec *pspec,
  201. GvcBalanceBar *bar)
  202. {
  203. update_balance_value (bar);
  204. }
  205. static gint
  206. find_stream_lfe_channel (MateMixerStreamControl *control)
  207. {
  208. guint i;
  209. for (i = 0; i < mate_mixer_stream_control_get_num_channels (control); i++) {
  210. MateMixerChannelPosition position;
  211. position = mate_mixer_stream_control_get_channel_position (control, i);
  212. if (position == MATE_MIXER_CHANNEL_LFE)
  213. return i;
  214. }
  215. return -1;
  216. }
  217. static void
  218. gvc_balance_bar_set_control (GvcBalanceBar *bar, MateMixerStreamControl *control)
  219. {
  220. g_return_if_fail (GVC_BALANCE_BAR (bar));
  221. g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control));
  222. if (bar->priv->control != NULL) {
  223. g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control),
  224. on_balance_value_changed,
  225. bar);
  226. g_object_unref (bar->priv->control);
  227. }
  228. bar->priv->control = g_object_ref (control);
  229. if (bar->priv->btype == BALANCE_TYPE_LFE) {
  230. gdouble minimum;
  231. gdouble maximum;
  232. minimum = mate_mixer_stream_control_get_min_volume (bar->priv->control);
  233. maximum = mate_mixer_stream_control_get_normal_volume (bar->priv->control);
  234. /* Configure the adjustment for the volume limits of the current
  235. * stream.
  236. * Only subwoofer scale uses volume, balance and fade use fixed
  237. * limits which do not need to be updated as balance type is
  238. * only set during construction. */
  239. gtk_adjustment_configure (GTK_ADJUSTMENT (bar->priv->adjustment),
  240. gtk_adjustment_get_value (bar->priv->adjustment),
  241. minimum,
  242. maximum,
  243. (maximum - minimum) / 100.0,
  244. (maximum - minimum) / 10.0,
  245. 0.0);
  246. bar->priv->lfe_channel = find_stream_lfe_channel (bar->priv->control);
  247. if (G_LIKELY (bar->priv->lfe_channel > -1))
  248. g_debug ("Found LFE channel at position %d", bar->priv->lfe_channel);
  249. else
  250. g_warn_if_reached ();
  251. } else
  252. bar->priv->lfe_channel = -1;
  253. switch (bar->priv->btype) {
  254. case BALANCE_TYPE_RL:
  255. g_signal_connect (G_OBJECT (bar->priv->control),
  256. "notify::balance",
  257. G_CALLBACK (on_balance_value_changed),
  258. bar);
  259. break;
  260. case BALANCE_TYPE_FR:
  261. g_signal_connect (G_OBJECT (bar->priv->control),
  262. "notify::fade",
  263. G_CALLBACK (on_balance_value_changed),
  264. bar);
  265. break;
  266. case BALANCE_TYPE_LFE:
  267. g_signal_connect (G_OBJECT (bar->priv->control),
  268. "notify::volume",
  269. G_CALLBACK (on_balance_value_changed),
  270. bar);
  271. break;
  272. }
  273. update_balance_value (bar);
  274. update_scale_marks (bar);
  275. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_CONTROL]);
  276. }
  277. static void
  278. gvc_balance_bar_set_balance_type (GvcBalanceBar *bar, GvcBalanceType btype)
  279. {
  280. GtkWidget *frame;
  281. GtkAdjustment *adjustment;
  282. /* Create adjustment with limits for balance and fade types because
  283. * some limits must be provided.
  284. * If subwoofer type is used instead, the limits will be changed when
  285. * stream is set. */
  286. adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -1.0, 1.0, 0.05, 0.5, 0.0));
  287. bar->priv->btype = btype;
  288. bar->priv->adjustment = GTK_ADJUSTMENT (g_object_ref_sink (adjustment));
  289. g_signal_connect (G_OBJECT (adjustment),
  290. "value-changed",
  291. G_CALLBACK (on_adjustment_value_changed),
  292. bar);
  293. switch (btype) {
  294. case BALANCE_TYPE_RL:
  295. bar->priv->label = gtk_label_new_with_mnemonic (_("_Balance:"));
  296. break;
  297. case BALANCE_TYPE_FR:
  298. bar->priv->label = gtk_label_new_with_mnemonic (_("_Fade:"));
  299. break;
  300. case BALANCE_TYPE_LFE:
  301. bar->priv->label = gtk_label_new_with_mnemonic (_("_Subwoofer:"));
  302. break;
  303. }
  304. #if GTK_CHECK_VERSION (3, 16, 0)
  305. gtk_label_set_xalign (GTK_LABEL (bar->priv->label), 0.0);
  306. gtk_label_set_yalign (GTK_LABEL (bar->priv->label), 0.0);
  307. #else
  308. gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.0);
  309. #endif
  310. /* Frame */
  311. frame = gtk_frame_new (NULL);
  312. gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
  313. gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0);
  314. /* Box with scale */
  315. create_scale_box (bar);
  316. gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box);
  317. gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label),
  318. bar->priv->scale);
  319. gtk_widget_set_direction (bar->priv->scale, GTK_TEXT_DIR_LTR);
  320. gtk_widget_show_all (frame);
  321. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_BALANCE_TYPE]);
  322. }
  323. static void
  324. gvc_balance_bar_set_property (GObject *object,
  325. guint prop_id,
  326. const GValue *value,
  327. GParamSpec *pspec)
  328. {
  329. GvcBalanceBar *self = GVC_BALANCE_BAR (object);
  330. switch (prop_id) {
  331. case PROP_CONTROL:
  332. gvc_balance_bar_set_control (self, g_value_get_object (value));
  333. break;
  334. case PROP_BALANCE_TYPE:
  335. gvc_balance_bar_set_balance_type (self, g_value_get_int (value));
  336. break;
  337. default:
  338. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  339. break;
  340. }
  341. }
  342. static void
  343. gvc_balance_bar_get_property (GObject *object,
  344. guint prop_id,
  345. GValue *value,
  346. GParamSpec *pspec)
  347. {
  348. GvcBalanceBar *self = GVC_BALANCE_BAR (object);
  349. switch (prop_id) {
  350. case PROP_CONTROL:
  351. g_value_set_object (value, self->priv->control);
  352. break;
  353. default:
  354. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  355. break;
  356. }
  357. }
  358. static void
  359. gvc_balance_bar_class_init (GvcBalanceBarClass *klass)
  360. {
  361. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  362. object_class->dispose = gvc_balance_bar_dispose;
  363. object_class->set_property = gvc_balance_bar_set_property;
  364. object_class->get_property = gvc_balance_bar_get_property;
  365. properties[PROP_CONTROL] =
  366. g_param_spec_object ("control",
  367. "Control",
  368. "MateMixer stream control",
  369. MATE_MIXER_TYPE_STREAM_CONTROL,
  370. G_PARAM_READWRITE |
  371. G_PARAM_STATIC_STRINGS);
  372. properties[PROP_BALANCE_TYPE] =
  373. g_param_spec_int ("balance-type",
  374. "balance type",
  375. "Whether the balance is right-left or front-rear",
  376. BALANCE_TYPE_RL,
  377. NUM_BALANCE_TYPES - 1,
  378. BALANCE_TYPE_RL,
  379. G_PARAM_READWRITE |
  380. G_PARAM_CONSTRUCT_ONLY |
  381. G_PARAM_STATIC_STRINGS);
  382. g_object_class_install_properties (object_class, N_PROPERTIES, properties);
  383. g_type_class_add_private (klass, sizeof (GvcBalanceBarPrivate));
  384. }
  385. static gboolean
  386. on_scale_scroll_event (GtkWidget *widget,
  387. GdkEventScroll *event,
  388. GvcBalanceBar *bar)
  389. {
  390. gdouble value;
  391. gdouble minimum;
  392. gdouble maximum;
  393. gdouble step;
  394. value = gtk_adjustment_get_value (bar->priv->adjustment);
  395. minimum = gtk_adjustment_get_lower (bar->priv->adjustment);
  396. maximum = gtk_adjustment_get_upper (bar->priv->adjustment);
  397. // XXX fix this for GTK3
  398. if (bar->priv->btype == BALANCE_TYPE_LFE)
  399. step = (maximum - minimum) / 100.0;
  400. else
  401. step = 0.05;
  402. if (event->direction == GDK_SCROLL_UP) {
  403. if (value + step > maximum)
  404. value = maximum;
  405. else
  406. value = value + step;
  407. } else if (event->direction == GDK_SCROLL_DOWN) {
  408. if (value - step < minimum)
  409. value = minimum;
  410. else
  411. value = value - step;
  412. }
  413. gtk_adjustment_set_value (bar->priv->adjustment, value);
  414. return TRUE;
  415. }
  416. static void
  417. on_adjustment_value_changed (GtkAdjustment *adjustment, GvcBalanceBar *bar)
  418. {
  419. gdouble value;
  420. if (bar->priv->control == NULL)
  421. return;
  422. value = gtk_adjustment_get_value (adjustment);
  423. switch (bar->priv->btype) {
  424. case BALANCE_TYPE_RL:
  425. mate_mixer_stream_control_set_balance (bar->priv->control, value);
  426. break;
  427. case BALANCE_TYPE_FR:
  428. mate_mixer_stream_control_set_fade (bar->priv->control, value);
  429. break;
  430. case BALANCE_TYPE_LFE:
  431. mate_mixer_stream_control_set_channel_volume (bar->priv->control,
  432. bar->priv->lfe_channel,
  433. value);
  434. break;
  435. }
  436. }
  437. static void
  438. gvc_balance_bar_init (GvcBalanceBar *bar)
  439. {
  440. bar->priv = GVC_BALANCE_BAR_GET_PRIVATE (bar);
  441. }
  442. static void
  443. gvc_balance_bar_dispose (GObject *object)
  444. {
  445. GvcBalanceBar *bar;
  446. bar = GVC_BALANCE_BAR (object);
  447. if (bar->priv->control != NULL) {
  448. g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control),
  449. on_balance_value_changed,
  450. bar);
  451. g_clear_object (&bar->priv->control);
  452. }
  453. G_OBJECT_CLASS (gvc_balance_bar_parent_class)->dispose (object);
  454. }
  455. GtkWidget *
  456. gvc_balance_bar_new (MateMixerStreamControl *control, GvcBalanceType btype)
  457. {
  458. return g_object_new (GVC_TYPE_BALANCE_BAR,
  459. "balance-type", btype,
  460. "control", control,
  461. #if GTK_CHECK_VERSION (3, 0, 0)
  462. "orientation", GTK_ORIENTATION_HORIZONTAL,
  463. #endif
  464. NULL);
  465. }