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.

gvc-channel-bar.c 44 KiB

4 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  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 "config.h"
  22. #include <glib.h>
  23. #include <glib/gi18n.h>
  24. #include <glib-object.h>
  25. #include <gtk/gtk.h>
  26. #include <canberra-gtk.h>
  27. #include <libmatemixer/matemixer.h>
  28. #include "gvc-channel-bar.h"
  29. #define SCALE_SIZE 128
  30. #define GVC_CHANNEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarPrivate))
  31. struct _GvcChannelBarPrivate
  32. {
  33. GtkOrientation orientation;
  34. GtkWidget *scale_box;
  35. GtkWidget *start_box;
  36. GtkWidget *end_box;
  37. GtkWidget *image;
  38. GtkWidget *label;
  39. GtkWidget *low_image;
  40. GtkWidget *scale;
  41. GtkWidget *high_image;
  42. GtkWidget *mute_box;
  43. GtkWidget *mute_button;
  44. GtkAdjustment *adjustment;
  45. gboolean show_icons;
  46. gboolean show_mute;
  47. gboolean show_marks;
  48. gboolean extended;
  49. GtkSizeGroup *size_group;
  50. gboolean symmetric;
  51. gboolean click_lock;
  52. MateMixerStreamControl *control;
  53. MateMixerStreamControlFlags control_flags;
  54. };
  55. enum {
  56. PROP_0,
  57. PROP_CONTROL,
  58. PROP_ORIENTATION,
  59. PROP_SHOW_ICONS,
  60. PROP_SHOW_MUTE,
  61. PROP_SHOW_MARKS,
  62. PROP_EXTENDED,
  63. PROP_NAME,
  64. PROP_ICON_NAME,
  65. PROP_LOW_ICON_NAME,
  66. PROP_HIGH_ICON_NAME,
  67. N_PROPERTIES
  68. };
  69. static GParamSpec *properties[N_PROPERTIES] = { NULL, };
  70. static void gvc_channel_bar_class_init (GvcChannelBarClass *klass);
  71. static void gvc_channel_bar_init (GvcChannelBar *bar);
  72. static gboolean on_scale_button_press_event (GtkWidget *widget,
  73. GdkEventButton *event,
  74. GvcChannelBar *bar);
  75. static gboolean on_scale_button_release_event (GtkWidget *widget,
  76. GdkEventButton *event,
  77. GvcChannelBar *bar);
  78. static gboolean on_scale_scroll_event (GtkWidget *widget,
  79. GdkEventScroll *event,
  80. GvcChannelBar *bar);
  81. G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_BOX)
  82. static void
  83. create_scale_box (GvcChannelBar *bar)
  84. {
  85. #if GTK_CHECK_VERSION (3, 0, 0)
  86. bar->priv->scale_box = gtk_box_new (bar->priv->orientation, 6);
  87. bar->priv->start_box = gtk_box_new (bar->priv->orientation, 6);
  88. bar->priv->end_box = gtk_box_new (bar->priv->orientation, 6);
  89. bar->priv->scale = gtk_scale_new (bar->priv->orientation,
  90. bar->priv->adjustment);
  91. #else
  92. if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) {
  93. bar->priv->scale_box = gtk_vbox_new (FALSE, 6);
  94. bar->priv->start_box = gtk_vbox_new (FALSE, 6);
  95. bar->priv->end_box = gtk_vbox_new (FALSE, 6);
  96. bar->priv->scale = gtk_vscale_new (bar->priv->adjustment);
  97. } else {
  98. bar->priv->scale_box = gtk_hbox_new (FALSE, 6);
  99. bar->priv->start_box = gtk_hbox_new (FALSE, 6);
  100. bar->priv->end_box = gtk_hbox_new (FALSE, 6);
  101. bar->priv->scale = gtk_hscale_new (bar->priv->adjustment);
  102. }
  103. #endif
  104. if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) {
  105. gtk_widget_set_size_request (bar->priv->scale, -1, SCALE_SIZE);
  106. gtk_range_set_inverted (GTK_RANGE (bar->priv->scale), TRUE);
  107. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  108. bar->priv->start_box,
  109. FALSE, FALSE, 0);
  110. gtk_box_pack_start (GTK_BOX (bar->priv->start_box),
  111. bar->priv->image,
  112. FALSE, FALSE, 0);
  113. gtk_box_pack_start (GTK_BOX (bar->priv->start_box),
  114. bar->priv->label,
  115. FALSE, FALSE, 0);
  116. gtk_box_pack_start (GTK_BOX (bar->priv->start_box),
  117. bar->priv->high_image,
  118. FALSE, FALSE, 0);
  119. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  120. bar->priv->scale,
  121. TRUE, TRUE, 0);
  122. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  123. bar->priv->end_box,
  124. FALSE, FALSE, 0);
  125. gtk_box_pack_start (GTK_BOX (bar->priv->end_box),
  126. bar->priv->low_image,
  127. FALSE, FALSE, 0);
  128. gtk_box_pack_start (GTK_BOX (bar->priv->end_box),
  129. bar->priv->mute_box,
  130. FALSE, FALSE, 0);
  131. } else {
  132. gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1);
  133. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  134. bar->priv->image,
  135. FALSE, FALSE, 0);
  136. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  137. bar->priv->start_box,
  138. FALSE, FALSE, 0);
  139. gtk_box_pack_end (GTK_BOX (bar->priv->start_box),
  140. bar->priv->low_image,
  141. FALSE, FALSE, 0);
  142. gtk_box_pack_start (GTK_BOX (bar->priv->start_box),
  143. bar->priv->label,
  144. TRUE, TRUE, 0);
  145. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  146. bar->priv->scale,
  147. TRUE, TRUE, 0);
  148. gtk_box_pack_start (GTK_BOX (bar->priv->scale_box),
  149. bar->priv->end_box,
  150. FALSE, FALSE, 0);
  151. gtk_box_pack_start (GTK_BOX (bar->priv->end_box),
  152. bar->priv->high_image,
  153. FALSE, FALSE, 0);
  154. gtk_box_pack_start (GTK_BOX (bar->priv->end_box),
  155. bar->priv->mute_box,
  156. FALSE, FALSE, 0);
  157. }
  158. if (bar->priv->show_icons) {
  159. gtk_widget_show (bar->priv->low_image);
  160. gtk_widget_show (bar->priv->high_image);
  161. } else {
  162. gtk_widget_hide (bar->priv->low_image);
  163. gtk_widget_hide (bar->priv->high_image);
  164. }
  165. ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE);
  166. gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK);
  167. g_signal_connect (G_OBJECT (bar->priv->scale),
  168. "button-press-event",
  169. G_CALLBACK (on_scale_button_press_event),
  170. bar);
  171. g_signal_connect (G_OBJECT (bar->priv->scale),
  172. "button-release-event",
  173. G_CALLBACK (on_scale_button_release_event),
  174. bar);
  175. g_signal_connect (G_OBJECT (bar->priv->scale),
  176. "scroll-event",
  177. G_CALLBACK (on_scale_scroll_event),
  178. bar);
  179. if (bar->priv->size_group != NULL) {
  180. gtk_size_group_add_widget (bar->priv->size_group,
  181. bar->priv->start_box);
  182. if (bar->priv->symmetric)
  183. gtk_size_group_add_widget (bar->priv->size_group,
  184. bar->priv->end_box);
  185. }
  186. gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE);
  187. }
  188. static void
  189. on_adjustment_value_changed (GtkAdjustment *adjustment,
  190. GvcChannelBar *bar)
  191. {
  192. gdouble value;
  193. gdouble lower;
  194. if (bar->priv->control == NULL || bar->priv->click_lock == TRUE)
  195. return;
  196. value = gtk_adjustment_get_value (bar->priv->adjustment);
  197. lower = gtk_adjustment_get_lower (bar->priv->adjustment);
  198. if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE)
  199. mate_mixer_stream_control_set_mute (bar->priv->control, (value <= lower));
  200. if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE)
  201. mate_mixer_stream_control_set_volume (bar->priv->control, (guint) value);
  202. }
  203. static void
  204. on_mute_button_toggled (GtkToggleButton *button, GvcChannelBar *bar)
  205. {
  206. gboolean mute;
  207. mute = gtk_toggle_button_get_active (button);
  208. mate_mixer_stream_control_set_mute (bar->priv->control, mute);
  209. }
  210. static void
  211. update_layout (GvcChannelBar *bar)
  212. {
  213. GtkWidget *frame;
  214. if (bar->priv->scale == NULL)
  215. return;
  216. frame = gtk_widget_get_parent (bar->priv->scale_box);
  217. g_object_ref (bar->priv->image);
  218. g_object_ref (bar->priv->label);
  219. g_object_ref (bar->priv->mute_box);
  220. g_object_ref (bar->priv->low_image);
  221. g_object_ref (bar->priv->high_image);
  222. // XXX this is not the opposite of what is done above
  223. gtk_container_remove (GTK_CONTAINER (bar->priv->start_box),
  224. bar->priv->image);
  225. gtk_container_remove (GTK_CONTAINER (bar->priv->start_box),
  226. bar->priv->label);
  227. gtk_container_remove (GTK_CONTAINER (bar->priv->end_box),
  228. bar->priv->mute_box);
  229. if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) {
  230. gtk_container_remove (GTK_CONTAINER (bar->priv->start_box),
  231. bar->priv->low_image);
  232. gtk_container_remove (GTK_CONTAINER (bar->priv->end_box),
  233. bar->priv->high_image);
  234. } else {
  235. gtk_container_remove (GTK_CONTAINER (bar->priv->end_box),
  236. bar->priv->low_image);
  237. gtk_container_remove (GTK_CONTAINER (bar->priv->start_box),
  238. bar->priv->high_image);
  239. }
  240. gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box),
  241. bar->priv->start_box);
  242. gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box),
  243. bar->priv->scale);
  244. gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box),
  245. bar->priv->end_box);
  246. gtk_container_remove (GTK_CONTAINER (frame),
  247. bar->priv->scale_box);
  248. create_scale_box (bar);
  249. gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box);
  250. g_object_unref (bar->priv->image);
  251. g_object_unref (bar->priv->label);
  252. g_object_unref (bar->priv->mute_box);
  253. g_object_unref (bar->priv->low_image);
  254. g_object_unref (bar->priv->high_image);
  255. gtk_widget_show_all (frame);
  256. }
  257. static void
  258. update_marks (GvcChannelBar *bar)
  259. {
  260. gdouble base;
  261. gdouble normal;
  262. gboolean has_mark = FALSE;
  263. gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale));
  264. if (bar->priv->control == NULL || bar->priv->show_marks == FALSE)
  265. return;
  266. /* Base volume represents unamplified volume, normal volume is the 100%
  267. * volume, in many cases they are the same as unamplified volume is unknown */
  268. base = mate_mixer_stream_control_get_base_volume (bar->priv->control);
  269. normal = mate_mixer_stream_control_get_normal_volume (bar->priv->control);
  270. if (normal <= gtk_adjustment_get_lower (bar->priv->adjustment))
  271. return;
  272. if (base < normal) {
  273. gchar *str = g_strdup_printf ("<small>%s</small>", C_("volume", "Unamplified"));
  274. gtk_scale_add_mark (GTK_SCALE (bar->priv->scale),
  275. base,
  276. GTK_POS_BOTTOM,
  277. str);
  278. has_mark = TRUE;
  279. g_free (str);
  280. }
  281. /* Only show 100% mark if the scale is extended beyond 100% and
  282. * there is no unamplified mark or it is below the normal volume */
  283. if (bar->priv->extended && (base == normal || base < normal)) {
  284. gchar *str = g_strdup_printf ("<small>%s</small>", C_("volume", "100%"));
  285. gtk_scale_add_mark (GTK_SCALE (bar->priv->scale),
  286. normal,
  287. GTK_POS_BOTTOM,
  288. str);
  289. has_mark = TRUE;
  290. g_free (str);
  291. }
  292. if (has_mark) {
  293. gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0, 0, 0);
  294. #if GTK_CHECK_VERSION (3, 0, 0)
  295. gtk_widget_set_halign (bar->priv->low_image, GTK_ALIGN_CENTER);
  296. gtk_widget_set_valign (bar->priv->low_image, GTK_ALIGN_START);
  297. gtk_widget_set_halign (bar->priv->high_image, GTK_ALIGN_CENTER);
  298. gtk_widget_set_valign (bar->priv->high_image, GTK_ALIGN_START);
  299. #else
  300. gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0.0);
  301. gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0.0);
  302. #endif
  303. #if GTK_CHECK_VERSION (3, 16, 0)
  304. gtk_label_set_xalign (GTK_LABEL (bar->priv->label), 0.0);
  305. gtk_label_set_yalign (GTK_LABEL (bar->priv->label), 0.0);
  306. #else
  307. gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.0);
  308. #endif
  309. } else {
  310. gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0.5, 0, 0);
  311. #if GTK_CHECK_VERSION (3, 0, 0)
  312. gtk_widget_set_halign (bar->priv->low_image, GTK_ALIGN_CENTER);
  313. gtk_widget_set_valign (bar->priv->low_image, GTK_ALIGN_CENTER);
  314. gtk_widget_set_halign (bar->priv->high_image, GTK_ALIGN_CENTER);
  315. gtk_widget_set_valign (bar->priv->high_image, GTK_ALIGN_CENTER);
  316. #else
  317. gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0.5);
  318. gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0.5);
  319. #endif
  320. #if GTK_CHECK_VERSION (3, 16, 0)
  321. gtk_label_set_xalign (GTK_LABEL (bar->priv->label), 0.0);
  322. gtk_label_set_yalign (GTK_LABEL (bar->priv->label), 0.5);
  323. #else
  324. gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.5);
  325. #endif
  326. }
  327. }
  328. static void
  329. update_adjustment_value (GvcChannelBar *bar)
  330. {
  331. gdouble value;
  332. gboolean set_lower = FALSE;
  333. /* Move the slider to the minimal value if the stream control is muted or
  334. * volume is unavailable */
  335. if (bar->priv->control == NULL)
  336. set_lower = TRUE;
  337. else if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE)
  338. set_lower = mate_mixer_stream_control_get_mute (bar->priv->control);
  339. if (set_lower == TRUE)
  340. value = gtk_adjustment_get_lower (bar->priv->adjustment);
  341. else
  342. value = mate_mixer_stream_control_get_volume (bar->priv->control);
  343. g_signal_handlers_block_by_func (G_OBJECT (bar->priv->adjustment),
  344. on_adjustment_value_changed,
  345. bar);
  346. gtk_adjustment_set_value (bar->priv->adjustment, value);
  347. g_signal_handlers_unblock_by_func (G_OBJECT (bar->priv->adjustment),
  348. on_adjustment_value_changed,
  349. bar);
  350. }
  351. static void
  352. update_adjustment_limits (GvcChannelBar *bar)
  353. {
  354. gdouble minimum = 0.0;
  355. gdouble maximum = 0.0;
  356. if (bar->priv->control != NULL) {
  357. minimum = mate_mixer_stream_control_get_min_volume (bar->priv->control);
  358. if (bar->priv->extended)
  359. maximum = mate_mixer_stream_control_get_max_volume (bar->priv->control);
  360. else
  361. maximum = mate_mixer_stream_control_get_normal_volume (bar->priv->control);
  362. }
  363. gtk_adjustment_configure (bar->priv->adjustment,
  364. gtk_adjustment_get_value (bar->priv->adjustment),
  365. minimum,
  366. maximum,
  367. (maximum - minimum) / 100.0,
  368. (maximum - minimum) / 15.0,
  369. 0.0);
  370. }
  371. static void
  372. update_mute_button (GvcChannelBar *bar)
  373. {
  374. if (bar->priv->show_mute == TRUE) {
  375. gboolean enable = FALSE;
  376. if (bar->priv->control != NULL &&
  377. bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE)
  378. enable = TRUE;
  379. if (enable == TRUE) {
  380. gboolean mute = mate_mixer_stream_control_get_mute (bar->priv->control);
  381. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button),
  382. mute);
  383. } else {
  384. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button),
  385. FALSE);
  386. }
  387. gtk_widget_set_sensitive (bar->priv->mute_button, enable);
  388. gtk_widget_show (bar->priv->mute_button);
  389. } else
  390. gtk_widget_hide (bar->priv->mute_button);
  391. }
  392. static gboolean
  393. on_scale_button_press_event (GtkWidget *widget,
  394. GdkEventButton *event,
  395. GvcChannelBar *bar)
  396. {
  397. #if !GTK_CHECK_VERSION (3, 6, 0)
  398. /* Up to GTK 3.4 the slider selection only moves in increments when
  399. * clicking in the slider with the left button and it moves directly
  400. * to the clicked position with the middle mouse button.
  401. * Change this behaviour to also jump to the clicked position with the
  402. * left mouse button. */
  403. if (event->button == 1)
  404. event->button = 2;
  405. #endif
  406. /* Muting the stream when volume is non-zero moves the slider to zero,
  407. * but the volume remains the same. In this case delay unmuting and
  408. * changing volume until user releases the mouse button. */
  409. if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE &&
  410. bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) {
  411. if (mate_mixer_stream_control_get_mute (bar->priv->control) == TRUE) {
  412. guint minimum = (guint) gtk_adjustment_get_lower (bar->priv->adjustment);
  413. if (mate_mixer_stream_control_get_volume (bar->priv->control) > minimum)
  414. bar->priv->click_lock = TRUE;
  415. }
  416. }
  417. return FALSE;
  418. }
  419. static gboolean
  420. on_scale_button_release_event (GtkWidget *widget,
  421. GdkEventButton *event,
  422. GvcChannelBar *bar)
  423. {
  424. #if !GTK_CHECK_VERSION (3, 6, 0)
  425. if (event->button == 1)
  426. event->button = 2;
  427. #endif
  428. if (bar->priv->click_lock == TRUE) {
  429. /* The volume change is not reflected while the lock is
  430. * held, propagate the change now that user has released
  431. * the mouse button */
  432. bar->priv->click_lock = FALSE;
  433. on_adjustment_value_changed (bar->priv->adjustment, bar);
  434. }
  435. /* Play a sound */
  436. ca_gtk_play_for_widget (GTK_WIDGET (bar), 0,
  437. CA_PROP_EVENT_ID, "audio-volume-change",
  438. CA_PROP_EVENT_DESCRIPTION, "Volume change",
  439. CA_PROP_APPLICATION_ID, "org.mate.VolumeControl",
  440. CA_PROP_APPLICATION_NAME, _("Volume Control"),
  441. CA_PROP_APPLICATION_VERSION, VERSION,
  442. CA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control",
  443. NULL);
  444. return FALSE;
  445. }
  446. static gboolean
  447. on_scale_scroll_event (GtkWidget *widget,
  448. GdkEventScroll *event,
  449. GvcChannelBar *bar)
  450. {
  451. GdkScrollDirection direction = event->direction;
  452. #if GTK_CHECK_VERSION (3, 4, 0)
  453. if (direction == GDK_SCROLL_SMOOTH) {
  454. gdouble dx = 0.0;
  455. gdouble dy = 0.0;
  456. gdk_event_get_scroll_deltas ((const GdkEvent *) event, &dx, &dy);
  457. if (dy > 0.0)
  458. direction = GDK_SCROLL_DOWN;
  459. else if (dy < 0.0)
  460. direction = GDK_SCROLL_UP;
  461. else
  462. return FALSE;
  463. }
  464. #endif
  465. return gvc_channel_bar_scroll (bar, direction);
  466. }
  467. static void
  468. on_control_volume_notify (MateMixerStreamControl *control,
  469. GParamSpec *pspec,
  470. GvcChannelBar *bar)
  471. {
  472. update_adjustment_value (bar);
  473. }
  474. static void
  475. on_control_mute_notify (MateMixerStreamControl *control,
  476. GParamSpec *pspec,
  477. GvcChannelBar *bar)
  478. {
  479. if (bar->priv->show_mute == TRUE) {
  480. gboolean mute = mate_mixer_stream_control_get_mute (control);
  481. g_signal_handlers_block_by_func (G_OBJECT (bar->priv->mute_button),
  482. on_mute_button_toggled,
  483. bar);
  484. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), mute);
  485. g_signal_handlers_unblock_by_func (G_OBJECT (bar->priv->mute_button),
  486. on_mute_button_toggled,
  487. bar);
  488. }
  489. update_adjustment_value (bar);
  490. }
  491. MateMixerStreamControl *
  492. gvc_channel_bar_get_control (GvcChannelBar *bar)
  493. {
  494. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL);
  495. return bar->priv->control;
  496. }
  497. void
  498. gvc_channel_bar_set_control (GvcChannelBar *bar, MateMixerStreamControl *control)
  499. {
  500. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  501. if (bar->priv->control == control)
  502. return;
  503. if (control != NULL)
  504. g_object_ref (control);
  505. if (bar->priv->control != NULL) {
  506. g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control),
  507. G_CALLBACK (on_control_volume_notify),
  508. bar);
  509. g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control),
  510. G_CALLBACK (on_control_mute_notify),
  511. bar);
  512. g_object_unref (bar->priv->control);
  513. }
  514. bar->priv->control = control;
  515. if (control != NULL)
  516. bar->priv->control_flags = mate_mixer_stream_control_get_flags (control);
  517. else
  518. bar->priv->control_flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS;
  519. if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE)
  520. g_signal_connect (G_OBJECT (control),
  521. "notify::volume",
  522. G_CALLBACK (on_control_volume_notify),
  523. bar);
  524. if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE)
  525. g_signal_connect (G_OBJECT (control),
  526. "notify::mute",
  527. G_CALLBACK (on_control_mute_notify),
  528. bar);
  529. update_marks (bar);
  530. update_mute_button (bar);
  531. update_adjustment_limits (bar);
  532. update_adjustment_value (bar);
  533. }
  534. GtkOrientation
  535. gvc_channel_bar_get_orientation (GvcChannelBar *bar)
  536. {
  537. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), 0);
  538. return bar->priv->orientation;
  539. }
  540. void
  541. gvc_channel_bar_set_orientation (GvcChannelBar *bar,
  542. GtkOrientation orientation)
  543. {
  544. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  545. if (orientation == bar->priv->orientation)
  546. return;
  547. bar->priv->orientation = orientation;
  548. update_layout (bar);
  549. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]);
  550. }
  551. gboolean
  552. gvc_channel_bar_get_show_icons (GvcChannelBar *bar)
  553. {
  554. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE);
  555. return bar->priv->show_icons;
  556. }
  557. void
  558. gvc_channel_bar_set_show_icons (GvcChannelBar *bar, gboolean show_icons)
  559. {
  560. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  561. if (show_icons == bar->priv->show_icons)
  562. return;
  563. bar->priv->show_icons = show_icons;
  564. if (bar->priv->show_icons == TRUE) {
  565. gtk_widget_show (bar->priv->low_image);
  566. gtk_widget_show (bar->priv->high_image);
  567. } else {
  568. gtk_widget_hide (bar->priv->low_image);
  569. gtk_widget_hide (bar->priv->high_image);
  570. }
  571. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_ICONS]);
  572. }
  573. gboolean
  574. gvc_channel_bar_get_show_mute (GvcChannelBar *bar)
  575. {
  576. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE);
  577. return bar->priv->show_mute;
  578. }
  579. void
  580. gvc_channel_bar_set_show_mute (GvcChannelBar *bar, gboolean show_mute)
  581. {
  582. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  583. if (show_mute == bar->priv->show_mute)
  584. return;
  585. bar->priv->show_mute = show_mute;
  586. update_mute_button (bar);
  587. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MUTE]);
  588. }
  589. gboolean
  590. gvc_channel_bar_get_show_marks (GvcChannelBar *bar)
  591. {
  592. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE);
  593. return bar->priv->show_marks;
  594. }
  595. void
  596. gvc_channel_bar_set_show_marks (GvcChannelBar *bar, gboolean show_marks)
  597. {
  598. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  599. if (show_marks == bar->priv->show_marks)
  600. return;
  601. bar->priv->show_marks = show_marks;
  602. update_marks (bar);
  603. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MARKS]);
  604. }
  605. gboolean
  606. gvc_channel_bar_get_extended (GvcChannelBar *bar)
  607. {
  608. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE);
  609. return bar->priv->extended;
  610. }
  611. void
  612. gvc_channel_bar_set_extended (GvcChannelBar *bar, gboolean extended)
  613. {
  614. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  615. if (extended == bar->priv->extended)
  616. return;
  617. bar->priv->extended = extended;
  618. /* Update displayed marks as non-extended scales do not show the 100%
  619. * limit at the end of the scale */
  620. update_marks (bar);
  621. update_adjustment_limits (bar);
  622. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_EXTENDED]);
  623. }
  624. const gchar *
  625. gvc_channel_bar_get_name (GvcChannelBar *bar)
  626. {
  627. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL);
  628. return gtk_label_get_text (GTK_LABEL (bar->priv->label));
  629. }
  630. void
  631. gvc_channel_bar_set_name (GvcChannelBar *bar, const gchar *name)
  632. {
  633. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  634. if (name != NULL) {
  635. gtk_label_set_text_with_mnemonic (GTK_LABEL (bar->priv->label), name);
  636. gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label),
  637. bar->priv->scale);
  638. gtk_widget_show (bar->priv->label);
  639. } else {
  640. gtk_label_set_text (GTK_LABEL (bar->priv->label), NULL);
  641. gtk_widget_hide (bar->priv->label);
  642. }
  643. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_NAME]);
  644. }
  645. const gchar *
  646. gvc_channel_bar_get_icon_name (GvcChannelBar *bar)
  647. {
  648. const gchar *name = NULL;
  649. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL);
  650. gtk_image_get_icon_name (GTK_IMAGE (bar->priv->image), &name, NULL);
  651. return name;
  652. }
  653. void
  654. gvc_channel_bar_set_icon_name (GvcChannelBar *bar, const gchar *name)
  655. {
  656. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  657. gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->image),
  658. name,
  659. GTK_ICON_SIZE_DIALOG);
  660. if (name != NULL)
  661. gtk_widget_show (bar->priv->image);
  662. else
  663. gtk_widget_hide (bar->priv->image);
  664. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ICON_NAME]);
  665. }
  666. const gchar *
  667. gvc_channel_bar_get_low_icon_name (GvcChannelBar *bar)
  668. {
  669. const gchar *name = NULL;
  670. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL);
  671. gtk_image_get_icon_name (GTK_IMAGE (bar->priv->low_image), &name, NULL);
  672. return name;
  673. }
  674. void
  675. gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, const gchar *name)
  676. {
  677. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  678. gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->low_image),
  679. name,
  680. GTK_ICON_SIZE_BUTTON);
  681. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_LOW_ICON_NAME]);
  682. }
  683. const gchar *
  684. gvc_channel_bar_get_high_icon_name (GvcChannelBar *bar)
  685. {
  686. const gchar *name = NULL;
  687. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL);
  688. gtk_image_get_icon_name (GTK_IMAGE (bar->priv->high_image), &name, NULL);
  689. return name;
  690. }
  691. void
  692. gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, const gchar *name)
  693. {
  694. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  695. gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->high_image),
  696. name,
  697. GTK_ICON_SIZE_BUTTON);
  698. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_HIGH_ICON_NAME]);
  699. }
  700. gboolean
  701. gvc_channel_bar_scroll (GvcChannelBar *bar, GdkScrollDirection direction)
  702. {
  703. gdouble value;
  704. gdouble minimum;
  705. gdouble maximum;
  706. gdouble scrollstep;
  707. g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE);
  708. if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) {
  709. if (direction != GDK_SCROLL_UP && direction != GDK_SCROLL_DOWN)
  710. return FALSE;
  711. } else {
  712. /* Switch direction for RTL */
  713. if (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_RTL) {
  714. if (direction == GDK_SCROLL_RIGHT)
  715. direction = GDK_SCROLL_LEFT;
  716. else if (direction == GDK_SCROLL_LEFT)
  717. direction = GDK_SCROLL_RIGHT;
  718. }
  719. /* Switch side scroll to vertical */
  720. if (direction == GDK_SCROLL_RIGHT)
  721. direction = GDK_SCROLL_UP;
  722. else if (direction == GDK_SCROLL_LEFT)
  723. direction = GDK_SCROLL_DOWN;
  724. }
  725. value = gtk_adjustment_get_value (bar->priv->adjustment);
  726. minimum = gtk_adjustment_get_lower (bar->priv->adjustment);
  727. maximum = gtk_adjustment_get_upper (bar->priv->adjustment);
  728. scrollstep = maximum / 100.0 * 5.0;
  729. if (direction == GDK_SCROLL_UP) {
  730. if (value + scrollstep > maximum)
  731. value = maximum;
  732. else
  733. value = value + scrollstep;
  734. } else if (direction == GDK_SCROLL_DOWN) {
  735. if (value - scrollstep < minimum)
  736. value = minimum;
  737. else
  738. value = value - scrollstep;
  739. }
  740. gtk_adjustment_set_value (bar->priv->adjustment, value);
  741. return TRUE;
  742. }
  743. void
  744. gvc_channel_bar_set_size_group (GvcChannelBar *bar,
  745. GtkSizeGroup *group,
  746. gboolean symmetric)
  747. {
  748. g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
  749. g_return_if_fail (GTK_IS_SIZE_GROUP (group));
  750. bar->priv->size_group = group;
  751. bar->priv->symmetric = symmetric;
  752. if (bar->priv->size_group != NULL) {
  753. gtk_size_group_add_widget (bar->priv->size_group,
  754. bar->priv->start_box);
  755. if (bar->priv->symmetric)
  756. gtk_size_group_add_widget (bar->priv->size_group,
  757. bar->priv->end_box);
  758. }
  759. gtk_widget_queue_draw (GTK_WIDGET (bar));
  760. }
  761. static void
  762. gvc_channel_bar_set_property (GObject *object,
  763. guint prop_id,
  764. const GValue *value,
  765. GParamSpec *pspec)
  766. {
  767. GvcChannelBar *self = GVC_CHANNEL_BAR (object);
  768. switch (prop_id) {
  769. case PROP_CONTROL:
  770. gvc_channel_bar_set_control (self, g_value_get_object (value));
  771. break;
  772. case PROP_ORIENTATION:
  773. gvc_channel_bar_set_orientation (self, g_value_get_enum (value));
  774. break;
  775. case PROP_SHOW_MUTE:
  776. gvc_channel_bar_set_show_mute (self, g_value_get_boolean (value));
  777. break;
  778. case PROP_SHOW_ICONS:
  779. gvc_channel_bar_set_show_icons (self, g_value_get_boolean (value));
  780. break;
  781. case PROP_SHOW_MARKS:
  782. gvc_channel_bar_set_show_marks (self, g_value_get_boolean (value));
  783. break;
  784. case PROP_EXTENDED:
  785. gvc_channel_bar_set_extended (self, g_value_get_boolean (value));
  786. break;
  787. case PROP_NAME:
  788. gvc_channel_bar_set_name (self, g_value_get_string (value));
  789. break;
  790. case PROP_ICON_NAME:
  791. gvc_channel_bar_set_icon_name (self, g_value_get_string (value));
  792. break;
  793. case PROP_LOW_ICON_NAME:
  794. gvc_channel_bar_set_low_icon_name (self, g_value_get_string (value));
  795. break;
  796. case PROP_HIGH_ICON_NAME:
  797. gvc_channel_bar_set_high_icon_name (self, g_value_get_string (value));
  798. break;
  799. default:
  800. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  801. break;
  802. }
  803. }
  804. static void
  805. gvc_channel_bar_get_property (GObject *object,
  806. guint prop_id,
  807. GValue *value,
  808. GParamSpec *pspec)
  809. {
  810. GvcChannelBar *self = GVC_CHANNEL_BAR (object);
  811. switch (prop_id) {
  812. case PROP_CONTROL:
  813. g_value_set_object (value, self->priv->control);
  814. break;
  815. case PROP_ORIENTATION:
  816. g_value_set_enum (value, self->priv->orientation);
  817. break;
  818. case PROP_SHOW_MUTE:
  819. g_value_set_boolean (value, self->priv->show_mute);
  820. break;
  821. case PROP_SHOW_ICONS:
  822. g_value_set_boolean (value, self->priv->show_icons);
  823. break;
  824. case PROP_SHOW_MARKS:
  825. g_value_set_boolean (value, self->priv->show_marks);
  826. break;
  827. case PROP_EXTENDED:
  828. g_value_set_boolean (value, self->priv->extended);
  829. break;
  830. case PROP_NAME:
  831. g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label)));
  832. break;
  833. default:
  834. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  835. break;
  836. }
  837. }
  838. static void
  839. gvc_channel_bar_class_init (GvcChannelBarClass *klass)
  840. {
  841. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  842. object_class->set_property = gvc_channel_bar_set_property;
  843. object_class->get_property = gvc_channel_bar_get_property;
  844. properties[PROP_CONTROL] =
  845. g_param_spec_object ("control",
  846. "Control",
  847. "MateMixer stream control",
  848. MATE_MIXER_TYPE_STREAM_CONTROL,
  849. G_PARAM_READWRITE |
  850. G_PARAM_CONSTRUCT |
  851. G_PARAM_STATIC_STRINGS);
  852. properties[PROP_ORIENTATION] =
  853. g_param_spec_enum ("orientation",
  854. "Orientation",
  855. "The orientation of the scale",
  856. GTK_TYPE_ORIENTATION,
  857. GTK_ORIENTATION_VERTICAL,
  858. G_PARAM_READWRITE |
  859. G_PARAM_STATIC_STRINGS);
  860. properties[PROP_SHOW_MUTE] =
  861. g_param_spec_boolean ("show-mute",
  862. "show mute",
  863. "Whether stream is muted",
  864. FALSE,
  865. G_PARAM_READWRITE |
  866. G_PARAM_CONSTRUCT |
  867. G_PARAM_STATIC_STRINGS);
  868. properties[PROP_SHOW_ICONS] =
  869. g_param_spec_boolean ("show-icons",
  870. "show mute",
  871. "Whether to show low and high icons",
  872. FALSE,
  873. G_PARAM_READWRITE |
  874. G_PARAM_CONSTRUCT |
  875. G_PARAM_STATIC_STRINGS);
  876. properties[PROP_SHOW_MARKS] =
  877. g_param_spec_boolean ("show-marks",
  878. "Show marks",
  879. "Whether to show scale marks",
  880. FALSE,
  881. G_PARAM_READWRITE |
  882. G_PARAM_CONSTRUCT |
  883. G_PARAM_STATIC_STRINGS);
  884. properties[PROP_EXTENDED] =
  885. g_param_spec_boolean ("extended",
  886. "Extended",
  887. "Allow the scale to be extended above normal volume",
  888. FALSE,
  889. G_PARAM_READWRITE |
  890. G_PARAM_STATIC_STRINGS);
  891. properties[PROP_NAME] =
  892. g_param_spec_string ("name",
  893. "Name",
  894. "Name to display for this stream",
  895. NULL,
  896. G_PARAM_READWRITE |
  897. G_PARAM_CONSTRUCT |
  898. G_PARAM_STATIC_STRINGS);
  899. properties[PROP_ICON_NAME] =
  900. g_param_spec_string ("icon-name",
  901. "Icon name",
  902. "Name of icon to display for this stream",
  903. NULL,
  904. G_PARAM_WRITABLE |
  905. G_PARAM_CONSTRUCT |
  906. G_PARAM_STATIC_STRINGS);
  907. properties[PROP_LOW_ICON_NAME] =
  908. g_param_spec_string ("low-icon-name",
  909. "Low icon name",
  910. "Name of low volume icon to display for this stream",
  911. "audio-volume-low",
  912. G_PARAM_WRITABLE |
  913. G_PARAM_CONSTRUCT |
  914. G_PARAM_STATIC_STRINGS);
  915. properties[PROP_HIGH_ICON_NAME] =
  916. g_param_spec_string ("high-icon-name",
  917. "High icon name",
  918. "Name of high volume icon to display for this stream",
  919. "audio-volume-high",
  920. G_PARAM_WRITABLE |
  921. G_PARAM_CONSTRUCT |
  922. G_PARAM_STATIC_STRINGS);
  923. g_object_class_install_properties (object_class, N_PROPERTIES, properties);
  924. g_type_class_add_private (klass, sizeof (GvcChannelBarPrivate));
  925. }
  926. static void
  927. gvc_channel_bar_init (GvcChannelBar *bar)
  928. {
  929. GtkWidget *frame;
  930. bar->priv = GVC_CHANNEL_BAR_GET_PRIVATE (bar);
  931. /* Mute button */
  932. bar->priv->mute_button = gtk_check_button_new_with_label (_("Mute"));
  933. gtk_widget_set_no_show_all (bar->priv->mute_button, TRUE);
  934. g_signal_connect (bar->priv->mute_button,
  935. "toggled",
  936. G_CALLBACK (on_mute_button_toggled),
  937. bar);
  938. bar->priv->mute_box = gtk_alignment_new (0.5, 0.5, 0, 0);
  939. gtk_container_add (GTK_CONTAINER (bar->priv->mute_box), bar->priv->mute_button);
  940. bar->priv->image = gtk_image_new ();
  941. gtk_widget_set_no_show_all (bar->priv->image, TRUE);
  942. /* Low/high icons */
  943. bar->priv->low_image = gtk_image_new ();
  944. gtk_widget_set_no_show_all (bar->priv->low_image, TRUE);
  945. bar->priv->high_image = gtk_image_new ();
  946. gtk_widget_set_no_show_all (bar->priv->high_image, TRUE);
  947. bar->priv->label = gtk_label_new (NULL);
  948. #if GTK_CHECK_VERSION (3, 16, 0)
  949. gtk_label_set_xalign (GTK_LABEL (bar->priv->label), 0.0);
  950. gtk_label_set_yalign (GTK_LABEL (bar->priv->label), 0.5);
  951. #else
  952. gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.5);
  953. #endif
  954. gtk_widget_set_no_show_all (bar->priv->label, TRUE);
  955. /* Frame */
  956. frame = gtk_frame_new (NULL);
  957. gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
  958. gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0);
  959. gtk_widget_show_all (frame);
  960. /* Create a default adjustment */
  961. bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 0, 0, 0, 0));
  962. g_object_ref_sink (bar->priv->adjustment);
  963. g_signal_connect (bar->priv->adjustment,
  964. "value-changed",
  965. G_CALLBACK (on_adjustment_value_changed),
  966. bar);
  967. /* Initially create a vertical scale box */
  968. bar->priv->orientation = GTK_ORIENTATION_VERTICAL;
  969. create_scale_box (bar);
  970. gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box);
  971. }
  972. GtkWidget *
  973. gvc_channel_bar_new (MateMixerStreamControl *control)
  974. {
  975. return g_object_new (GVC_TYPE_CHANNEL_BAR,
  976. "control", control,
  977. #if GTK_CHECK_VERSION (3, 0, 0)
  978. "orientation", GTK_ORIENTATION_HORIZONTAL,
  979. #endif
  980. NULL);
  981. }