From 93332554c235c7de59c64d0733f791a3be9f1775 Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Sat, 8 Nov 2014 15:14:10 +0100 Subject: Move some files and directories to better locations - mate-volume-control/data changed to /data - mate-volume-control/src/* moved to mate-volume-control - sound-theme/sounds changed to /data/sounds - sound-theme/*.{c,h} moved to mate-volume-control - Removed AUTHORS and ChangeLog.pre-2-26 from mate-volume-control --- mate-volume-control/AUTHORS | 2 - mate-volume-control/ChangeLog.pre-2-26 | 1245 ---------- mate-volume-control/Makefile.am | 76 +- mate-volume-control/applet-main.c | 94 + mate-volume-control/data/Makefile.am | 29 - mate-volume-control/data/icons/16x16/Makefile.am | 4 - .../data/icons/16x16/status/Makefile.am | 21 - .../16x16/status/audio-input-microphone-high.png | Bin 853 -> 0 bytes .../16x16/status/audio-input-microphone-low.png | Bin 548 -> 0 bytes .../16x16/status/audio-input-microphone-medium.png | Bin 687 -> 0 bytes .../16x16/status/audio-input-microphone-muted.png | Bin 520 -> 0 bytes mate-volume-control/data/icons/22x22/Makefile.am | 4 - .../data/icons/22x22/status/Makefile.am | 21 - .../22x22/status/audio-input-microphone-high.png | Bin 1188 -> 0 bytes .../22x22/status/audio-input-microphone-low.png | Bin 827 -> 0 bytes .../22x22/status/audio-input-microphone-medium.png | Bin 968 -> 0 bytes .../22x22/status/audio-input-microphone-muted.png | Bin 817 -> 0 bytes mate-volume-control/data/icons/24x24/Makefile.am | 4 - .../data/icons/24x24/status/Makefile.am | 21 - .../24x24/status/audio-input-microphone-high.png | Bin 1214 -> 0 bytes .../24x24/status/audio-input-microphone-low.png | Bin 849 -> 0 bytes .../24x24/status/audio-input-microphone-medium.png | Bin 1001 -> 0 bytes .../24x24/status/audio-input-microphone-muted.png | Bin 851 -> 0 bytes mate-volume-control/data/icons/32x32/Makefile.am | 4 - .../data/icons/32x32/status/Makefile.am | 21 - .../32x32/status/audio-input-microphone-high.png | Bin 2182 -> 0 bytes .../32x32/status/audio-input-microphone-low.png | Bin 1818 -> 0 bytes .../32x32/status/audio-input-microphone-medium.png | Bin 1981 -> 0 bytes .../32x32/status/audio-input-microphone-muted.png | Bin 2005 -> 0 bytes mate-volume-control/data/icons/Makefile.am | 25 - .../data/icons/scalable/Makefile.am | 4 - .../data/icons/scalable/devices/Makefile.am | 35 - .../devices/audio-speaker-center-back-testing.svg | 539 ---- .../scalable/devices/audio-speaker-center-back.svg | 506 ---- .../devices/audio-speaker-center-testing.svg | 537 ---- .../scalable/devices/audio-speaker-center.svg | 504 ---- .../devices/audio-speaker-left-back-testing.svg | 537 ---- .../scalable/devices/audio-speaker-left-back.svg | 504 ---- .../devices/audio-speaker-left-side-testing.svg | 537 ---- .../scalable/devices/audio-speaker-left-side.svg | 504 ---- .../devices/audio-speaker-left-testing.svg | 537 ---- .../icons/scalable/devices/audio-speaker-left.svg | 504 ---- .../devices/audio-speaker-right-back-testing.svg | 537 ---- .../scalable/devices/audio-speaker-right-back.svg | 504 ---- .../devices/audio-speaker-right-side-testing.svg | 537 ---- .../scalable/devices/audio-speaker-right-side.svg | 504 ---- .../devices/audio-speaker-right-testing.svg | 913 ------- .../icons/scalable/devices/audio-speaker-right.svg | 504 ---- .../scalable/devices/audio-subwoofer-testing.svg | 240 -- .../icons/scalable/devices/audio-subwoofer.svg | 325 --- .../data/icons/scalable/status/Makefile.am | 21 - .../status/audio-input-microphone-high.svg | 1076 -------- .../scalable/status/audio-input-microphone-low.svg | 1045 -------- .../status/audio-input-microphone-medium.svg | 1066 -------- .../status/audio-input-microphone-muted.svg | 1040 -------- .../data/mate-volume-control-applet.desktop.in | 17 - .../data/mate-volume-control.desktop.in.in | 15 - mate-volume-control/dialog-main.c | 274 ++ mate-volume-control/gvc-applet.c | 357 +++ mate-volume-control/gvc-applet.h | 61 + mate-volume-control/gvc-balance-bar.c | 552 +++++ mate-volume-control/gvc-balance-bar.h | 82 + mate-volume-control/gvc-channel-bar.c | 1158 +++++++++ mate-volume-control/gvc-channel-bar.h | 118 + mate-volume-control/gvc-combo-box.c | 397 +++ mate-volume-control/gvc-combo-box.h | 79 + mate-volume-control/gvc-level-bar.c | 876 +++++++ mate-volume-control/gvc-level-bar.h | 79 + mate-volume-control/gvc-mixer-dialog.c | 2606 ++++++++++++++++++++ mate-volume-control/gvc-mixer-dialog.h | 65 + mate-volume-control/gvc-sound-theme-chooser.c | 1147 +++++++++ mate-volume-control/gvc-sound-theme-chooser.h | 62 + mate-volume-control/gvc-speaker-test.c | 517 ++++ mate-volume-control/gvc-speaker-test.h | 71 + mate-volume-control/gvc-stream-status-icon.c | 805 ++++++ mate-volume-control/gvc-stream-status-icon.h | 68 + mate-volume-control/gvc-utils.c | 335 +++ mate-volume-control/gvc-utils.h | 42 + mate-volume-control/sound-theme-file-utils.c | 305 +++ mate-volume-control/sound-theme-file-utils.h | 37 + mate-volume-control/src/Makefile.am | 73 - mate-volume-control/src/applet-main.c | 94 - mate-volume-control/src/dialog-main.c | 274 -- mate-volume-control/src/gvc-applet.c | 357 --- mate-volume-control/src/gvc-applet.h | 61 - mate-volume-control/src/gvc-balance-bar.c | 552 ----- mate-volume-control/src/gvc-balance-bar.h | 82 - mate-volume-control/src/gvc-channel-bar.c | 1158 --------- mate-volume-control/src/gvc-channel-bar.h | 118 - mate-volume-control/src/gvc-combo-box.c | 397 --- mate-volume-control/src/gvc-combo-box.h | 79 - mate-volume-control/src/gvc-level-bar.c | 876 ------- mate-volume-control/src/gvc-level-bar.h | 79 - mate-volume-control/src/gvc-mixer-dialog.c | 2606 -------------------- mate-volume-control/src/gvc-mixer-dialog.h | 65 - mate-volume-control/src/gvc-speaker-test.c | 517 ---- mate-volume-control/src/gvc-speaker-test.h | 71 - mate-volume-control/src/gvc-stream-status-icon.c | 805 ------ mate-volume-control/src/gvc-stream-status-icon.h | 68 - mate-volume-control/src/gvc-utils.c | 335 --- mate-volume-control/src/gvc-utils.h | 42 - 101 files changed, 10256 insertions(+), 23709 deletions(-) delete mode 100644 mate-volume-control/AUTHORS delete mode 100644 mate-volume-control/ChangeLog.pre-2-26 create mode 100644 mate-volume-control/applet-main.c delete mode 100644 mate-volume-control/data/Makefile.am delete mode 100644 mate-volume-control/data/icons/16x16/Makefile.am delete mode 100644 mate-volume-control/data/icons/16x16/status/Makefile.am delete mode 100644 mate-volume-control/data/icons/16x16/status/audio-input-microphone-high.png delete mode 100644 mate-volume-control/data/icons/16x16/status/audio-input-microphone-low.png delete mode 100644 mate-volume-control/data/icons/16x16/status/audio-input-microphone-medium.png delete mode 100644 mate-volume-control/data/icons/16x16/status/audio-input-microphone-muted.png delete mode 100644 mate-volume-control/data/icons/22x22/Makefile.am delete mode 100644 mate-volume-control/data/icons/22x22/status/Makefile.am delete mode 100644 mate-volume-control/data/icons/22x22/status/audio-input-microphone-high.png delete mode 100644 mate-volume-control/data/icons/22x22/status/audio-input-microphone-low.png delete mode 100644 mate-volume-control/data/icons/22x22/status/audio-input-microphone-medium.png delete mode 100644 mate-volume-control/data/icons/22x22/status/audio-input-microphone-muted.png delete mode 100644 mate-volume-control/data/icons/24x24/Makefile.am delete mode 100644 mate-volume-control/data/icons/24x24/status/Makefile.am delete mode 100644 mate-volume-control/data/icons/24x24/status/audio-input-microphone-high.png delete mode 100644 mate-volume-control/data/icons/24x24/status/audio-input-microphone-low.png delete mode 100644 mate-volume-control/data/icons/24x24/status/audio-input-microphone-medium.png delete mode 100644 mate-volume-control/data/icons/24x24/status/audio-input-microphone-muted.png delete mode 100644 mate-volume-control/data/icons/32x32/Makefile.am delete mode 100644 mate-volume-control/data/icons/32x32/status/Makefile.am delete mode 100644 mate-volume-control/data/icons/32x32/status/audio-input-microphone-high.png delete mode 100644 mate-volume-control/data/icons/32x32/status/audio-input-microphone-low.png delete mode 100644 mate-volume-control/data/icons/32x32/status/audio-input-microphone-medium.png delete mode 100644 mate-volume-control/data/icons/32x32/status/audio-input-microphone-muted.png delete mode 100644 mate-volume-control/data/icons/Makefile.am delete mode 100644 mate-volume-control/data/icons/scalable/Makefile.am delete mode 100644 mate-volume-control/data/icons/scalable/devices/Makefile.am delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-center-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-center.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-left-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-left.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-right-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-speaker-right.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-subwoofer-testing.svg delete mode 100644 mate-volume-control/data/icons/scalable/devices/audio-subwoofer.svg delete mode 100644 mate-volume-control/data/icons/scalable/status/Makefile.am delete mode 100644 mate-volume-control/data/icons/scalable/status/audio-input-microphone-high.svg delete mode 100644 mate-volume-control/data/icons/scalable/status/audio-input-microphone-low.svg delete mode 100644 mate-volume-control/data/icons/scalable/status/audio-input-microphone-medium.svg delete mode 100644 mate-volume-control/data/icons/scalable/status/audio-input-microphone-muted.svg delete mode 100644 mate-volume-control/data/mate-volume-control-applet.desktop.in delete mode 100644 mate-volume-control/data/mate-volume-control.desktop.in.in create mode 100644 mate-volume-control/dialog-main.c create mode 100644 mate-volume-control/gvc-applet.c create mode 100644 mate-volume-control/gvc-applet.h create mode 100644 mate-volume-control/gvc-balance-bar.c create mode 100644 mate-volume-control/gvc-balance-bar.h create mode 100644 mate-volume-control/gvc-channel-bar.c create mode 100644 mate-volume-control/gvc-channel-bar.h create mode 100644 mate-volume-control/gvc-combo-box.c create mode 100644 mate-volume-control/gvc-combo-box.h create mode 100644 mate-volume-control/gvc-level-bar.c create mode 100644 mate-volume-control/gvc-level-bar.h create mode 100644 mate-volume-control/gvc-mixer-dialog.c create mode 100644 mate-volume-control/gvc-mixer-dialog.h create mode 100644 mate-volume-control/gvc-sound-theme-chooser.c create mode 100644 mate-volume-control/gvc-sound-theme-chooser.h create mode 100644 mate-volume-control/gvc-speaker-test.c create mode 100644 mate-volume-control/gvc-speaker-test.h create mode 100644 mate-volume-control/gvc-stream-status-icon.c create mode 100644 mate-volume-control/gvc-stream-status-icon.h create mode 100644 mate-volume-control/gvc-utils.c create mode 100644 mate-volume-control/gvc-utils.h create mode 100644 mate-volume-control/sound-theme-file-utils.c create mode 100644 mate-volume-control/sound-theme-file-utils.h delete mode 100644 mate-volume-control/src/Makefile.am delete mode 100644 mate-volume-control/src/applet-main.c delete mode 100644 mate-volume-control/src/dialog-main.c delete mode 100644 mate-volume-control/src/gvc-applet.c delete mode 100644 mate-volume-control/src/gvc-applet.h delete mode 100644 mate-volume-control/src/gvc-balance-bar.c delete mode 100644 mate-volume-control/src/gvc-balance-bar.h delete mode 100644 mate-volume-control/src/gvc-channel-bar.c delete mode 100644 mate-volume-control/src/gvc-channel-bar.h delete mode 100644 mate-volume-control/src/gvc-combo-box.c delete mode 100644 mate-volume-control/src/gvc-combo-box.h delete mode 100644 mate-volume-control/src/gvc-level-bar.c delete mode 100644 mate-volume-control/src/gvc-level-bar.h delete mode 100644 mate-volume-control/src/gvc-mixer-dialog.c delete mode 100644 mate-volume-control/src/gvc-mixer-dialog.h delete mode 100644 mate-volume-control/src/gvc-speaker-test.c delete mode 100644 mate-volume-control/src/gvc-speaker-test.h delete mode 100644 mate-volume-control/src/gvc-stream-status-icon.c delete mode 100644 mate-volume-control/src/gvc-stream-status-icon.h delete mode 100644 mate-volume-control/src/gvc-utils.c delete mode 100644 mate-volume-control/src/gvc-utils.h (limited to 'mate-volume-control') diff --git a/mate-volume-control/AUTHORS b/mate-volume-control/AUTHORS deleted file mode 100644 index 3f6dd9e..0000000 --- a/mate-volume-control/AUTHORS +++ /dev/null @@ -1,2 +0,0 @@ -William Jon McCann -Bastien Nocera diff --git a/mate-volume-control/ChangeLog.pre-2-26 b/mate-volume-control/ChangeLog.pre-2-26 deleted file mode 100644 index 8b38808..0000000 --- a/mate-volume-control/ChangeLog.pre-2-26 +++ /dev/null @@ -1,1245 +0,0 @@ -2009-03-16 Marc-André Lureau - - * src/Makefile.am (*_SOURCES): Fix make dist. Include gvc-log.h. - -2009-03-12 Bastien Nocera - - * src/gvc-stream-status-icon.c (on_stream_decibel_notify), - (gvc_stream_status_icon_set_mixer_stream): Also update the - tooltip when decibels change, fixes the dB value pretty much - always being wrong (the code notifies of the changed dB after the - changed volume) (Closes: #575093) - -2009-03-12 Bastien Nocera - - * src/gvc-balance-bar.c (_scale_box_new), - (gvc_balance_bar_set_balance_type), (gvc_balance_bar_set_property), - (gvc_balance_bar_class_init), (on_adjustment_value_changed), - (gvc_balance_bar_init), (gvc_balance_bar_new): Fix use of the - zero_adjustment GtkAdjustment in slider callbacks, export - gvc_channel_bar_scroll - * src/gvc-balance-bar.h: Export gvc_channel_bar_scroll - - * src/gvc-stream-status-icon.c (on_status_icon_scroll_event): - Use gvc_channel_bar_scroll instead of our own code - - (Closes: #564313) - -2009-03-06 Matthias Clasen - - * src/applet-main.c: - * src/dialog-main.c: Wire up --debug - - * src/gvc-log.[hc]: Add some trivial logging infrastructure - - * src/Makefile.am: Add gvc-log.c - -2009-03-04 Bastien Nocera - - * src/Makefile.am: Link to libsoundtheme.la, not using - -l against a static lib - -==================== 2.25.92 ==================== - -2009-02-23 Matthias Clasen - - Bug 572665 – update sensitivity of input feedback checkbox - - * src/gvc-sound-theme-chooser.c (update_theme): Make the window - and button sounds button insensitive too. - -2009-02-18 Bastien Nocera - - * src/gvc-balance-bar.c (_scale_box_new), - (on_scale_button_press_event), (on_scale_button_release_event), - (on_scale_scroll_event): Make the balance bar instant-apply - (Closes: #566423) - -2009-02-18 Bastien Nocera - - * src/gvc-stream-status-icon.c (popup_dock): Fix warning - -2009-02-18 Bastien Nocera - - * src/gvc-stream-status-icon.c (popup_dock), - (gvc_stream_status_icon_constructor): Patch from Yanko Kaneti - to fix the dock showing up off-screen - when the applet is at the bottom of the screen and is popped - up for the first time (Closes: #564312) - -2009-02-17 Bastien Nocera - - * src/gvc-mixer-control.c (update_source): Don't filter out - non-hardware sources, only ignore sink monitors - -2009-02-17 Bastien Nocera - - * src/gvc-mixer-control.c (update_sink): Don't filter out - sinks with PA_SINK_HARDWARE, otherwise things like network sinks - (Airport Express?) won't show up in the volume capplet - -2009-02-17 Bastien Nocera - - * src/gvc-channel-bar.c (on_scale_scroll_event), - (gvc_channel_bar_set_is_amplified), (gvc_channel_bar_set_property), - (gvc_channel_bar_get_property), (gvc_channel_bar_class_init), - (gvc_channel_bar_init): - * src/gvc-channel-bar.h: Add "is-amplified" property. In this mode, - the volume can be set above 100% (150% tops actually) for inputs - that support giving us the dB information - * src/gvc-mixer-dialog.c (on_mixer_control_default_source_changed), - (add_stream): Disconnect the value-changed signal, and - reconnect it when we're done setting is_amplified for source streams - that support giving dB info - (Closes: #567536) - -2009-02-17 Bastien Nocera - - * src/gvc-channel-map.c (gvc_channel_map_can_balance), - (set_from_pa_map): - * src/gvc-channel-map.h: Add API to detect whether a channel - map supports balancing - * src/gvc-mixer-dialog.c (update_output_settings): De-sensitivise - the balance bar if the output doesn't support balacing - -2009-02-17 Bastien Nocera - - * src/gvc-balance-bar.c (on_adjustment_value_changed): - Remove fixed FIXME - -2009-02-17 Bastien Nocera - - * src/applet-main.c: Remove unused macro - -2009-02-12 Bastien Nocera - - * src/gvc-mixer-control.c (gvc_stream_collate): Fix run-time - warning when there's no name for a stream, such as some events - from libcanberra - -2009-02-12 Bastien Nocera - - * src/dialog-main.c (on_control_ready), (warning_dialog_answered), - (dialog_popup_timeout), (main): When PulseAudio isn't running, - or takes a long time to connect to, popup a dialogue telling - the user what we're waiting for, and the ability to cancel - (Closes: #566835) - -2009-02-11 Bastien Nocera - - * src/applet-main.c (main): Use libunique instead of D-Bus - and refuse to run if an applet is already running - * src/dialog-main.c (message_received_cb), (on_control_ready), - (main): present the application when it's already running - (Closes: #542727) - -2009-02-11 Bastien Nocera - - * src/gvc-channel-bar.c (on_scale_button_release_event): - * src/gvc-sound-theme-chooser.c (play_preview_for_path): - * src/gvc-sound-theme-editor.c (play_sound_preview), - (play_sound_at_path): Don't call ca_gtk_context_get () when - we're not going to use the returned context, set the - application id for all the calls to ca_gtk_play_*() - -2009-02-10 Bastien Nocera - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - Correct the label to mention that there are no recording apps as well - (Closes: #568900) - -2009-02-10 Bastien Nocera - - * data/Makefile.am: Install the applet's desktop file in the - xdg autostart directory (Closes: #570114) - -2009-02-10 Bastien Nocera - - * src/gvc-mixer-control.c (update_source_output): - * src/gvc-mixer-dialog.c (add_stream): Show recording applications - in addition to playback applications, use the microphone icon - for those applications (Closes: #568900) - -2009-02-10 Bastien Nocera - - * src/gvc-applet.c (maybe_show_status_icons): - * src/gvc-mixer-control.c (set_application_id_from_proplist), - (update_sink_input), (update_source_output): - * src/gvc-mixer-dialog.c (create_monitor_stream_for_source): - * src/gvc-mixer-stream.c (gvc_mixer_stream_get_application_id), - (gvc_mixer_stream_set_application_id), - (gvc_mixer_stream_set_property), (gvc_mixer_stream_get_property), - (gvc_mixer_stream_class_init), (gvc_mixer_stream_finalize): - * src/gvc-mixer-stream.h: Set the org.mate.VolumeControl application - ID for the input peak monitor, and ignore it when checking whether - an application is using the input. Also ignore Pavucontrol. - Stops the microphone showing up in the applet when the capplet is - running (Closes: #565146) - -2009-02-10 Bastien Nocera - - * src/gvc-applet.c (maybe_show_status_icons): Revert accidentally - committing the rejected patch for bug #565146 - -2009-01-26 Bastien Nocera - - * src/gvc-balance-bar.c (gvc_balance_bar_init): Make sure the balance - bar stays Left to Right, so that the Left balance is really on the - left - -2009-01-26 Bastien Nocera - - * src/gvc-balance-bar.c (_scale_box_new), (gvc_balance_bar_init): - Patch from Luca Ferretti , to use the new GTK+ - scale marks for the balance widget (Closes: #565144) - -2009-01-26 Bastien Nocera - - * src/dialog-main.c (on_dialog_response), (on_dialog_close): - Use gtk_main_quit () instead of exit (), so that all the - objects are unref'ed cleanly - -2009-01-26 Bastien Nocera - - * src/dialog-main.c (on_dialog_response), (on_dialog_close), - (on_control_ready): Exit when Esc is pressed - -2009-01-26 Bastien Nocera - - * src/gvc-level-bar.c (gvc_level_bar_finalize): - Remove any lingering timeouts when finalizing the object - -2009-01-26 Bastien Nocera - - * src/Makefile.am: Fix automake warning - - * src/gvc-applet.c (maybe_show_status_icons): - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - * src/gvc-stream-status-icon.c (on_status_icon_scroll_event), - (update_icon), (gvc_stream_status_icon_init): Remove ifdef's - from the volume control code, as GTK+ 2.15.1 is now required - (Closes: #564318) - -2009-01-24 Luca Ferretti - - * data/mate-volume-control.desktop.in.in: - Icon name is mate-volume-control, not mate-sound-properties. - -2009-01-23 Bastien Nocera - - * data/icons/16x16/apps/Makefile.am: - * data/icons/22x22/apps/Makefile.am: - * data/icons/24x24/apps/Makefile.am: - * data/icons/32x32/apps/Makefile.am: - * data/icons/48x48/apps/Makefile.am: - * data/icons/scalable/apps/Makefile.am: - Install the application icon in the global - icon theme, not in our private directory - (Closes: #568635) - - * data/icons/Makefile.am: Don't run gtk-update-icon-cache - for a private icons directory - -2009-01-20 Bastien Nocera - - * data/Makefile.am: - * data/icons/48x48/apps/Makefile.am: - * data/icons/Makefile.am: - * src/Makefile.am: Fix distcheck - -2009-01-20 Bastien Nocera - - * src/gvc-channel-bar.c (_scale_box_new), - (on_scale_button_press_event), (on_scale_button_release_event), - (on_scale_scroll_event), (gvc_channel_bar_init): Move the - setup of the press and release button events to _scale_box_new(), - otherwise it's not handled with horizontal scales. - When scrolling with the mouse wheel, swap the up/down directions - to work as expected (Closes: #567034) - -2009-01-20 Bastien Nocera - - * src/gvc-mixer-control.c (set_is_event_stream_from_proplist), - (update_sink_input): - * src/gvc-mixer-stream.c (gvc_mixer_stream_is_event_stream), - (gvc_mixer_stream_set_is_event_stream), - (gvc_mixer_stream_set_property), (gvc_mixer_stream_get_property), - (gvc_mixer_stream_class_init): - * src/gvc-mixer-stream.h: Add the "is-event-stream" property, - to check whether the role of a stream is to play an event - - * src/gvc-mixer-dialog.c (add_stream): Don't show event streams - in the applications tab (Closes: #565145) - -2009-01-20 Bastien Nocera - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - Add Close button, and change the spacing around it to - match existing capplets (Closes: #564314) - -2009-01-20 Bastien Nocera - - * data/mate-volume-control-applet.desktop.in: Start - mate-volume-control after the panel, not at the same time, - spotted by Chris Coulson (Closes: #568320) - -2009-01-16 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - Don't show no apps message if there are apps. - Fixes #567250 - -2009-01-16 William Jon McCann - - * src/gvc-stream-status-icon.c - (gvc_stream_status_icon_constructor): - Reduce border around volume slider dock - -2009-01-16 William Jon McCann - - * src/gvc-channel-bar.c (_scale_box_new), - (on_scale_button_press_event), (on_scale_button_release_event), - (on_zero_adjustment_value_changed), (gvc_channel_bar_init): - Make volume sliders instantaneous. - - Patch from: Bastien Nocera - Fixes #564313 - -2009-01-16 William Jon McCann - - * src/gvc-stream-status-icon.c (on_menu_mute_toggled), - (on_status_icon_popup_menu): - Add a mute toggle item to the context menu. - - Patch from: Paolo Borelli - Fixes #567510 - -2009-01-16 William Jon McCann - - * src/gvc-applet.c (maybe_show_status_icons): - Don't leak a list of sources. - - Patch from: Paolo Borelli - Fixes #567514 - -2009-01-10 William Jon McCann - - * src/gvc-balance-bar.c (_scale_box_new): - Don't paint the scale trough. - Patch from: Matthias Clasen - Fixes part of #565144 - -2008-12-21 William Jon McCann - - * src/gvc-stream-status-icon.c (popup_dock): - addresses a bunch of positioning problems - Patch from: Matthias Clasen - Fixes #564312 - -2008-12-21 William Jon McCann - - * src/gvc-sound-theme-chooser.c (on_treeview_selection_changed): - Check for null selection. - -2008-12-21 William Jon McCann - - * data/sounds/Makefile.am: - * data/sounds/mate-sounds-default.xml.in.in: - Add drip, glass, and sonar sounds. - -2008-12-21 William Jon McCann - - * data/Makefile.am: - * data/sounds/Makefile.am: - * data/sounds/mate-sounds-default.xml.in.in: - * src/Makefile.am: - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - * src/gvc-sound-theme-chooser.c (set_combox_for_theme_name), - (xml_get_and_trim_names), (populate_model_from_node), - (populate_model_from_file), (populate_model_from_dir), - (save_alert_sounds), (update_alert_model), (update_alert), - (on_alert_toggled), (play_preview_for_path), - (create_alert_treeview), (get_file_type), - (update_alerts_from_theme_name), (update_theme), - (gvc_sound_theme_chooser_init): - * src/sound-theme-file-utils.c (custom_theme_dir_is_empty), - (delete_old_files), (delete_disabled_files), (add_disabled_file), - (add_custom_file), (create_custom_theme): - * src/sound-theme-file-utils.h: - Add sound set. Properly set the active alert sound in the - model. - -2008-12-21 William Jon McCann - - * src/gvc-sound-theme-chooser.c (on_combobox_changed), - (set_input_feedback_enabled), (update_theme), (on_alert_toggled), - (play_preview_for_path), (on_treeview_row_activated), - (on_treeview_selection_changed), (create_alert_treeview), - (gvc_sound_theme_chooser_constructor), - (gvc_sound_theme_chooser_init): - * src/gvc-sound-theme-editor.c (theme_changed_custom_reinit), - (on_theme_changed), (load_index_theme_name), (sound_theme_in_dir), - (add_theme_to_store), (set_theme_name), (audible_bell_foreach), - (set_audible_bell_enabled), (input_feedback_foreach), - (set_input_feedback_enabled), (get_file_type), - (theme_changed_custom_init), (update_theme), - (setup_theme_selector), (play_sound_preview), (get_sound_filename), - (count_customised_sounds), (save_sounds), (save_custom_theme), - (dump_theme), (on_setting_column_edited), (fill_custom_model), - (on_combobox_editing_started), (play_sound_at_path), - (setting_set_func), (activatable_cell_renderer_pixbuf_activate), - (activatable_cell_renderer_pixbuf_init), - (activatable_cell_renderer_pixbuf_class_init), - (setup_theme_custom_selector), - (gvc_sound_theme_editor_constructor), - (gvc_sound_theme_editor_class_init), (on_click_feedback_toggled), - (on_key_changed), (on_treeview_row_activated), - (constrain_list_size), (setup_list_size_constraint), - (gvc_sound_theme_editor_init), (gvc_sound_theme_editor_finalize), - (gvc_sound_theme_editor_new): - * src/gvc-sound-theme-editor.h: - Simplify the theme selection. Move the ability to - customize the sound theme into a separate widget. We - should add a Customize button to access it. - This new approach will mirror the way we handle - wallpapers. Next thing to do is to add a set - of standard alert sounds that one can choose from. - And have the widget customize the selected theme to - use them. - -2008-12-21 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - Move the sound effects tab back to the first one. - -2008-12-19 Bastien Nocera - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - Move the sound effects tab to be the last one - -2008-12-19 Bastien Nocera - - * src/gvc-sound-theme-chooser.c (setup_theme_custom_selector): - Change the ellipsising to be at the start - -2008-12-19 Bastien Nocera - - * data/mate-volume-control.desktop.in.in: - Add a .desktop file to launch g-v-c from the - control-center (and preferences menu) - - * data/Makefile.am: changes for the above - - * data/icons/16x16/Makefile.am: - * data/icons/16x16/apps/*: - * data/icons/22x22/Makefile.am: - * data/icons/22x22/apps/*: - * data/icons/24x24/Makefile.am: - * data/icons/24x24/apps/*: - * data/icons/32x32/Makefile.am: - * data/icons/32x32/apps/*: - * data/icons/48x48/*: - * data/icons/scalable/Makefile.am: - * data/icons/scalable/apps/*: - Add icons for the .desktop file - -2008-12-18 Bastien Nocera - - * src/gvc-sound-theme-chooser.c (setup_theme_custom_selector): - Set the first column's text to be ellipsised, and expanded, - fixes the window resizing on startup, but still some unsightly - resizing in the treeview itself - -2008-12-17 Bastien Nocera - - * AUTHORS: Add myself, having written much of src/*theme*.[ch] - -2008-12-16 William Jon McCann - - * src/gvc-level-bar.c (gvc_level_bar_size_allocate): - Recalculate layout for allocation. - -2008-12-16 William Jon McCann - - * src/gvc-level-bar.c (layout_changed), (reset_max_peak), - (bar_calc_layout), (update_peak_value), (update_rms_value), - (gvc_level_bar_expose): - Separate the layout calculation from the draw. - Only queue a redraw if the layout actually changes. - Reduces cpu usage considerably. - -2008-12-14 William Jon McCann - - * src/gvc-level-bar.c (gvc_level_bar_expose): - Compress expose event queue. - -2008-12-14 William Jon McCann - - * src/gvc-balance-bar.c (_scale_box_new): - Add left and right labels. - -2008-12-14 William Jon McCann - - * src/gvc-level-bar.c (gvc_level_bar_expose): - Round corners a bit more. - -2008-12-14 William Jon McCann - - * src/gvc-level-bar.c (reset_max_peak), (update_peak_value), - (gvc_level_bar_expose): - Add peak of level indicator. - -2008-12-14 William Jon McCann - - * src/Makefile.am: - * src/gvc-level-bar.c (fraction_from_adjustment), - (update_peak_value), (update_rms_value), - (gvc_level_bar_get_orientation), (gvc_level_bar_set_orientation), - (on_peak_adjustment_value_changed), - (on_rms_adjustment_value_changed), - (gvc_level_bar_set_peak_adjustment), - (gvc_level_bar_set_rms_adjustment), - (gvc_level_bar_get_peak_adjustment), - (gvc_level_bar_get_rms_adjustment), (gvc_level_bar_set_scale), - (gvc_level_bar_set_property), (gvc_level_bar_get_property), - (gvc_level_bar_constructor), (gvc_level_bar_size_request), - (gvc_level_bar_size_allocate), (curved_rectangle), - (gvc_level_bar_expose), (gvc_level_bar_class_init), - (gvc_level_bar_init), (gvc_level_bar_finalize), - (gvc_level_bar_new): - * src/gvc-level-bar.h: - * src/gvc-mixer-dialog.c (update_input_peak), - (gvc_mixer_dialog_constructor): - * src/rb-segmented-bar.c: - * src/rb-segmented-bar.h: - Remove rb-segmented-bar and add a new GvcLevelBar. - -2008-12-13 William Jon McCann - - * src/Makefile.am: - * src/gvc-balance-bar.c (_scale_box_new), - (gvc_balance_bar_set_size_group), - (gvc_balance_bar_set_channel_map), (gvc_balance_bar_set_property), - (gvc_balance_bar_get_property), (gvc_balance_bar_constructor), - (gvc_balance_bar_class_init), (on_left), (on_right), - (on_adjustment_value_changed), (gvc_balance_bar_init), - (gvc_balance_bar_finalize), (gvc_balance_bar_new): - * src/gvc-balance-bar.h: - * src/gvc-channel-map.c (gvc_channel_map_class_init), - (gvc_channel_map_gains_changed), (set_from_pa_map): - * src/gvc-channel-map.h: - * src/gvc-mixer-control.c (gvc_mixer_control_set_default_sink), - (gvc_mixer_control_set_default_source), - (update_default_source_from_name), (update_default_sink_from_name), - (update_sink), (update_source), (update_sink_input), - (update_event_role_stream): - * src/gvc-mixer-dialog.c (update_output_settings), - (on_mixer_control_default_sink_changed), (add_stream), - (on_output_radio_toggled), (gvc_mixer_dialog_constructor): - * src/gvc-mixer-sink-input.c (gvc_mixer_sink_input_change_volume): - * src/gvc-mixer-sink.c (gvc_mixer_sink_change_volume): - * src/gvc-mixer-source.c (gvc_mixer_source_change_volume): - * src/gvc-mixer-stream.c (on_channel_map_gains_changed), - (gvc_mixer_stream_set_channel_map): - Add output balance control. - -2008-12-13 William Jon McCann - - * AUTHORS: updated email address - -2008-12-13 William Jon McCann - - * src/Makefile.am: - * src/gvc-channel-map.c (gvc_channel_map_get_num_channels), - (gvc_channel_map_get_gains), (gvc_channel_map_get_positions), - (gvc_channel_map_class_init), (gvc_channel_map_init), - (gvc_channel_map_finalize), (gvc_channel_map_new), - (set_from_pa_map), (gvc_channel_map_new_from_pa_channel_map): - * src/gvc-channel-map.h: - * src/gvc-mixer-control.c (update_sink), (update_source), - (update_sink_input), (update_source_output): - * src/gvc-mixer-event-role.c (update_settings), - (gvc_mixer_event_role_new): - * src/gvc-mixer-sink-input.c (gvc_mixer_sink_input_change_volume), - (gvc_mixer_sink_input_new): - * src/gvc-mixer-sink-input.h: - * src/gvc-mixer-sink.c (gvc_mixer_sink_change_volume), - (gvc_mixer_sink_new): - * src/gvc-mixer-sink.h: - * src/gvc-mixer-source-output.c (gvc_mixer_source_output_new): - * src/gvc-mixer-source-output.h: - * src/gvc-mixer-source.c (gvc_mixer_source_change_volume), - (gvc_mixer_source_new): - * src/gvc-mixer-source.h: - * src/gvc-mixer-stream.c (gvc_mixer_stream_get_channel_map), - (gvc_mixer_stream_set_channel_map), - (gvc_mixer_stream_set_property), (gvc_mixer_stream_get_property), - (gvc_mixer_stream_class_init): - * src/gvc-mixer-stream.h: - Add a ChannelMap class that we can use to manipulate channel - volume levels. - -2008-12-13 William Jon McCann - - * src/gvc-channel-bar.c (_scale_box_new), (update_layout), - (gvc_channel_bar_set_size_group), - (gvc_channel_bar_set_orientation), (gvc_channel_bar_constructor), - (gvc_channel_bar_init): - * src/gvc-channel-bar.h: - * src/gvc-mixer-dialog.c (create_bar), (add_stream), - (gvc_mixer_dialog_constructor): - Don't put the end box in the size group for app channel bars. - -2008-12-13 William Jon McCann - - * src/gvc-mixer-dialog.c (add_stream), (remove_stream), - (gvc_mixer_dialog_constructor): - Show explanation when no apps are playing. - -2008-12-13 William Jon McCann - - * src/Makefile.am: - * src/gvc-channel-bar.c (_scale_box_new): - * src/gvc-mixer-control.c (gvc_mixer_control_get_pa_context), - (update_default_source_from_name): - * src/gvc-mixer-control.h: - * src/gvc-mixer-dialog.c (update_input_peak), (update_input_meter), - (on_monitor_suspended_callback), (on_monitor_read_callback), - (create_monitor_stream_for_source), - (on_mixer_control_default_source_changed), (add_stream), - (gvc_mixer_dialog_constructor): - * src/rb-segmented-bar.c (rb_segment_new), (rb_segment_free), - (rb_segmented_bar_init), (rb_segmented_bar_class_init), - (rb_segmented_bar_finalize), (rb_segmented_bar_get_property), - (rb_segmented_bar_set_property), - (rb_segmented_bar_default_value_formatter), - (rb_segmented_bar_size_request), (create_adapt_layout), - (compute_layout_size), (rb_segmented_bar_size_allocate), - (rb_segmented_bar_add_segment), - (rb_segmented_bar_add_segment_default_color), - (rb_segmented_bar_update_segment), (draw_rounded_rectangle), - (rb_segmented_bar_render_segments), (modula), (hsb_from_color), - (color_from_hsb), (color_shade), (make_segment_gradient), - (rb_segmented_bar_render_strokes), (rb_segmented_bar_render), - (rb_segmented_bar_render_labels), (rb_segmented_bar_expose), - (rb_segmented_bar_new), (rb_segmented_bar_set_value_formatter): - * src/rb-segmented-bar.h: - Add input level monitor. - -2008-12-13 William Jon McCann - - * src/gvc-channel-bar.c (_scale_box_new), - (gvc_channel_bar_set_orientation): - Use the same size group for the start and end of - the channel bar. - -2008-12-12 Bastien Nocera - - * src/gvc-sound-theme-chooser.c (get_sound_filename): - Set a parent for the file chooser - -2008-12-12 Bastien Nocera - - * src/gvc-stream-status-icon.c (update_icon): Fix - last commit, we need to use commas, not dots to separate - major, minor and micro versions - -2007-12-12 Matthias Clasen - - Bug 564176 – dependency on API from gtk+ trunk - - * mate-volume-control/src/gvc-stream-status-icon.c: Don't use - GTK+ 2.16 API unconditionally. Reported by Frederic Peters - -2008-12-12 William Jon McCann - - * src/gvc-mixer-control.c (update_sink), (update_source): - * src/gvc-mixer-stream.c (gvc_mixer_stream_get_decibel), - (gvc_mixer_stream_set_decibel), (gvc_mixer_stream_get_can_decibel), - (gvc_mixer_stream_set_can_decibel), - (gvc_mixer_stream_set_property), (gvc_mixer_stream_get_property), - (gvc_mixer_stream_class_init): - * src/gvc-mixer-stream.h: - * src/gvc-stream-status-icon.c (update_icon): - Add decibel level support. - -2008-12-11 William Jon McCann - - * src/Makefile.am: - * src/gvc-applet.c (maybe_show_status_icons), (gvc_applet_start), - (on_control_stream_removed), (on_control_stream_added), - (gvc_applet_constructor): - * src/gvc-mixer-control.c (gvc_mixer_control_get_source_outputs), - (update_source), (update_sink_input), (update_source_output), - (update_event_role_stream): - * src/gvc-mixer-control.h: - * src/gvc-mixer-source-output.c - (gvc_mixer_source_output_change_volume), - (gvc_mixer_source_output_change_is_muted), - (gvc_mixer_source_output_constructor), - (gvc_mixer_source_output_class_init), - (gvc_mixer_source_output_init), (gvc_mixer_source_output_finalize), - (gvc_mixer_source_output_new): - * src/gvc-mixer-source-output.h: - * src/gvc-stream-status-icon.c (update_icon), - (on_status_icon_visible_notify), (gvc_stream_status_icon_init): - Show/hide input level status icon based on presence of - a "source output" stream. - -2008-12-11 William Jon McCann - - * src/gvc-channel-bar.c (gvc_channel_bar_set_low_icon_name), - (gvc_channel_bar_set_high_icon_name), - (gvc_channel_bar_set_property), (gvc_channel_bar_get_property), - (gvc_channel_bar_class_init), (gvc_channel_bar_init), - (gvc_channel_bar_finalize): - * src/gvc-channel-bar.h: - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor), - (gvc_mixer_dialog_set_page): - Use the new input icons in the dialog. - -2008-12-11 William Jon McCann - - * src/Makefile.am: - * src/applet-main.c (main): - * src/dialog-main.c (main): - Append the icon search path to the icon theme. - -2008-12-11 William Jon McCann - - * src/gvc-applet.c: - Use new icons. - -2008-12-11 William Jon McCann - - * data/Makefile.am: - * data/icons/*: - Add some input level icons from Mike Langlie. Just - an initial draft. - -2008-12-11 William Jon McCann - - * src/Makefile.am: - * src/gvc-applet.c (gvc_applet_start), (gvc_applet_dispose), - (update_default_source), (update_default_sink), (on_control_ready), - (on_control_default_sink_changed), - (on_control_default_source_changed), (gvc_applet_constructor), - (gvc_applet_class_init), (gvc_applet_init), (gvc_applet_finalize): - * src/gvc-stream-status-icon.c (on_adjustment_value_changed), - (popup_dock), (on_status_icon_activate), - (on_menu_activate_open_volume_control), - (on_status_icon_popup_menu), (on_status_icon_scroll_event), - (gvc_icon_release_grab), (on_dock_button_press), (popdown_dock), - (gvc_icon_grab_notify), (on_dock_grab_notify), - (on_dock_grab_broken_event), (on_dock_key_release), - (maybe_show_status_icon), (update_icon), - (gvc_stream_status_icon_set_icon_names), (on_stream_volume_notify), - (on_stream_is_muted_notify), - (gvc_stream_status_icon_set_display_name), - (gvc_stream_status_icon_set_mixer_stream), - (gvc_stream_status_icon_set_property), - (gvc_stream_status_icon_get_property), (on_bar_is_muted_notify), - (gvc_stream_status_icon_constructor), - (gvc_stream_status_icon_dispose), - (gvc_stream_status_icon_class_init), (gvc_stream_status_icon_init), - (gvc_stream_status_icon_finalize), (gvc_stream_status_icon_new): - * src/gvc-stream-status-icon.h: - Create a stream-status-icon class. Create a status icon - for input stream. Todo: only show input status icon - when an application is recording, use microphone images. - -2008-12-05 William Jon McCann - - * src/gvc-channel-bar.c (gvc_channel_bar_init): - Make label left aligned. - -2008-12-05 William Jon McCann - - * src/gvc-mixer-control.c (update_sink), (update_sink_input), - (update_client), (remove_client), (gvc_mixer_control_init): - Use application/client name instead of stream name. - -2008-12-05 Marc-André Lureau - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_set_page): - Added --page argument for compatibility reasons (used by g-s-r, - for instance). Closes: #563289 - -2008-11-19 William Jon McCann - - * src/gvc-mixer-control.c (update_sink_input), - (update_event_role_stream): - Add debug for event stuff. - -2008-11-19 William Jon McCann - - * src/gvc-mixer-dialog.c (on_stream_volume_notify), - (on_stream_is_muted_notify): - Fix crash when can't find a bar for stream. - -2008-11-19 William Jon McCann - - * src/gvc-mixer-dialog.c (update_default_input), - (update_default_output), (on_mixer_control_default_sink_changed), - (on_mixer_control_default_source_changed), (add_stream), - (on_input_radio_toggled), (on_output_radio_toggled), - (create_stream_treeview), (gvc_mixer_dialog_constructor): - Use radio buttons instead of selection to change default in/out. - -2008-11-19 William Jon McCann - - * src/gvc-channel-bar.c (_scale_box_new), (gvc_channel_bar_new): - * src/gvc-mixer-control.c (_set_default_source), - (_set_default_sink): - Make range update policy discontinuous. - -2008-11-19 William Jon McCann - - * src/gvc-sound-theme-chooser.c (constrain_list_size), - (setup_list_size_constraint), (gvc_sound_theme_chooser_init): - Make the initial size of the scrolled window more reasonable. - -2008-11-18 William Jon McCann - - * src/gvc-mixer-dialog.c (create_stream_treeview): - * src/gvc-sound-theme-chooser.c (play_sound_at_path), - (activatable_cell_renderer_pixbuf_activate), - (setup_theme_custom_selector), (on_treeview_row_activated), - (gvc_sound_theme_chooser_init): - Hide treeview headers. Make double click on tree rows - play sound preview. - -2008-11-18 William Jon McCann - - * src/gvc-applet.c (maybe_show_status_icon), (update_icon), - (update_default_sink), (on_control_ready), - (on_control_default_sink_changed), (gvc_applet_init): - * src/gvc-mixer-control.c (remove_stream): - Hide the status icon when there is no output device. - -2008-11-18 William Jon McCann - - * src/gvc-mixer-control.c (gvc_mixer_control_set_default_sink), - (gvc_mixer_control_set_default_source), (_set_default_source), - (_set_default_sink), (_stream_has_name), (find_stream_for_name), - (update_default_source_from_name), (update_default_sink_from_name), - (update_server), (update_sink), (update_source), - (update_sink_input), (gvc_mixer_control_class_init): - * src/gvc-mixer-control.h: - * src/gvc-mixer-dialog.c (on_mixer_control_default_sink_changed), - (on_mixer_control_default_source_changed), - (gvc_mixer_dialog_set_mixer_control), (bar_set_stream), - (add_stream), (find_stream_by_id), (remove_stream), - (on_control_stream_removed), (create_stream_treeview), - (on_input_treeview_selection_changed), - (on_output_treeview_selection_changed), - (gvc_mixer_dialog_constructor): - * src/gvc-mixer-stream.c (gvc_mixer_stream_set_property), - (gvc_mixer_stream_get_property), (gvc_mixer_stream_class_init): - * src/gvc-mixer-stream.h: - Add support for changing the default sink and source. - -2008-11-12 William Jon McCann - - * src/Makefile.am: - * src/gvc-mixer-control.c (update_event_role_stream): - * src/gvc-mixer-dialog.c (create_bar), (add_stream): - * src/gvc-mixer-event-role.c (update_settings), - (gvc_mixer_event_role_change_volume), - (gvc_mixer_event_role_change_is_muted), - (gvc_mixer_event_role_set_device), - (gvc_mixer_event_role_set_property), - (gvc_mixer_event_role_get_property), - (gvc_mixer_event_role_constructor), - (gvc_mixer_event_role_class_init), (gvc_mixer_event_role_init), - (gvc_mixer_event_role_finalize), (gvc_mixer_event_role_new): - * src/gvc-mixer-event-role.h: - Add event role stream. Make setting event volume work. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor), - (gvc_mixer_dialog_init): - * src/gvc-sound-theme-chooser.c (theme_changed_custom_reinit), - (on_combobox_changed), (audible_bell_foreach), - (set_audible_bell_enabled), (input_feedback_foreach), - (set_input_feedback_enabled), (get_file_type), - (theme_changed_custom_init), (update_theme), - (setup_theme_selector), (count_customised_sounds), (save_sounds), - (on_setting_column_edited), (fill_custom_model), - (on_combobox_editing_started), (setting_set_func), - (setup_theme_custom_selector), - (gvc_sound_theme_chooser_constructor), (on_click_feedback_toggled), - (on_key_changed), (gvc_sound_theme_chooser_init), - (gvc_sound_theme_chooser_new): - Simplify the theme chooser a bit. Remove the visual bell stuff - since that should go into the to-be-written Universal Access - control panel. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (on_key_changed): - Don't assume all changed keys are booleans. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (create_bar), (add_stream), - (gvc_mixer_dialog_constructor), (gvc_mixer_dialog_init): - Use a separate size group for application streams. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - Switch order of input and output tabs. - -2008-11-12 William Jon McCann - - * src/gvc-channel-bar.c (_scale_box_new): - Add labels to size group instead of scale because - using expand/fill widgets in a size group doesn't really - work. And in practice the label is the only variable - length widget. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_dispose): - Only disconnect signals once. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - * src/gvc-sound-theme-chooser.c (gvc_sound_theme_chooser_init): - Move alert and input sliders to the top. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - * src/gvc-sound-theme-chooser.c (setup_theme_selector), - (gvc_sound_theme_chooser_init): - Drop the frame and label from the effects chooser. - Move the enable effects toggle up to the top. - -2008-11-12 William Jon McCann - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): - At mpt's suggestion, move the output slider to the top. - -2008-11-11 William Jon McCann - - * src/gvc-mixer-control.c (update_sink_input), - (update_source_output), (update_event_role_stream), - (remove_client), (remove_sink), (remove_source), - (remove_sink_input), (remove_source_output): - * src/gvc-mixer-dialog.c (on_bar_is_muted_notify), (create_bar), - (bar_set_stream), (add_stream), (on_control_stream_added), - (on_control_stream_removed): - Set the associated stream on the bar so mute works. - Hush some debugging. - -2008-11-11 William Jon McCann - - * src/gvc-mixer-control.c (update_sink), (update_source): - * src/gvc-mixer-dialog.c (add_stream): - * src/gvc-mixer-stream.c (gvc_mixer_stream_get_description), - (gvc_mixer_stream_set_description), - (gvc_mixer_stream_set_property), (gvc_mixer_stream_get_property), - (gvc_mixer_stream_class_init), (gvc_mixer_stream_finalize): - * src/gvc-mixer-stream.h: - Add descriptions and use them instead of names when - displaying sinks and sources. - -2008-11-11 William Jon McCann - - * src/gvc-mixer-dialog.c (add_stream): - Don't show sinks/source in apps page. - -2008-11-11 William Jon McCann - - * src/gvc-mixer-dialog.c (add_stream), (find_stream_by_id), - (on_control_stream_removed), (create_stream_treeview): - Try to remove streams from models. - -2008-11-11 William Jon McCann - - * src/gvc-mixer-dialog.c (add_stream): - Also add default streams to tree models. - -2008-11-11 William Jon McCann - - * src/gvc-mixer-control.c (gvc_mixer_control_get_streams): - * src/gvc-mixer-control.h: - * src/gvc-mixer-dialog.c (create_bar), (bar_set_stream), - (add_stream), (create_stream_treeview), - (gvc_mixer_dialog_constructor): - Add beginnings of output and input selection. - -2008-11-10 William Jon McCann - - * src/gvc-mixer-dialog.c (add_stream), - (gvc_mixer_dialog_constructor): - Don't crash if we don't get a default sink/source. - -2008-11-06 William Jon McCann - - * src/gvc-mixer-control.c (remove_stream), (update_sink_input), - (update_event_role_stream): - * src/gvc-mixer-dialog.c (on_control_stream_added), - (on_control_stream_removed): - Fix adding and remove streams. - -2008-11-06 William Jon McCann - - * src/gvc-mixer-dialog.c (_gtk_label_make_bold), - (gvc_mixer_dialog_constructor): - * src/gvc-sound-theme-chooser.c (gvc_sound_theme_chooser_init): - Tweak layout and add some placeholder treeviews. - -2008-11-06 William Jon McCann - - * src/gvc-channel-bar.c (_scale_box_new), - (gvc_channel_bar_set_size_group): - * src/gvc-channel-bar.h: - * src/gvc-mixer-dialog.c (add_stream), (gvc_mixer_dialog_init): - Put all the scales in a size group. Doesn't seem - to work though. - -2008-11-06 William Jon McCann - - * src/gvc-mixer-dialog.c (on_enable_effects_toggled), - (on_click_feedback_toggled), (on_audible_bell_toggled), - (gvc_mixer_dialog_constructor), (on_key_changed), - (gvc_mixer_dialog_init): - Hook up the check buttons. - -2008-11-06 William Jon McCann - - * src/gvc-mixer-dialog.c (add_stream), - (gvc_mixer_dialog_constructor): - * src/gvc-sound-theme-chooser.c (setup_theme_selector), - (gvc_sound_theme_chooser_init): - Fix up some layout. Hide the theme selector if there is - only one. - -2008-11-06 William Jon McCann - - * src/Makefile.am: - * src/gvc-applet.c (on_menu_activate_open_volume_control), - (on_status_icon_popup_menu): - * src/gvc-mixer-dialog.c (on_stream_is_muted_notify), (add_stream), - (gvc_mixer_dialog_constructor), (gvc_mixer_dialog_new): - * src/gvc-sound-theme-chooser.c (theme_changed_custom_reinit), - (on_combobox_changed), (load_index_theme_name), - (sound_theme_in_dir), (add_theme_to_store), - (set_combox_for_theme_name), (on_theme_changed), - (setup_theme_selector), (visual_bell_mateconf_to_setting), - (visual_bell_setting_to_mateconf), (play_sound_preview), - (get_sound_filename), (count_customised_sounds), (save_sounds), - (save_custom_theme), (dump_theme), (on_setting_column_edited), - (fill_visual_bell_model), (fill_custom_model), - (on_combobox_editing_started), (play_sound_at_path), - (on_treeview_button_press_event), (setting_set_func), - (activatable_cell_renderer_pixbuf_activate), - (activatable_cell_renderer_pixbuf_init), - (activatable_cell_renderer_pixbuf_class_init), - (input_feedback_foreach), (set_input_feedback_enabled), - (on_input_feedback_changed), (audible_bell_foreach), - (set_audible_bell_enabled), (on_audible_bell_changed), - (get_file_type), (theme_changed_custom_init), - (setup_theme_custom_selector), - (gvc_sound_theme_chooser_constructor), - (gvc_sound_theme_chooser_class_init), (_gtk_label_make_bold), - (gvc_sound_theme_chooser_init), (gvc_sound_theme_chooser_finalize), - (gvc_sound_theme_chooser_new): - * src/gvc-sound-theme-chooser.h: - * src/sound-theme-file-utils.c (custom_theme_update_time), - (custom_theme_dir_path), (directory_delete_recursive), - (capplet_file_delete_recursive), (delete_custom_theme_dir), - (delete_one_file), (delete_old_files), (delete_disabled_files), - (create_one_file), (add_disabled_file), (add_custom_file): - * src/sound-theme-file-utils.h: - Redesign a bit in order to incorporate the sound properties. - -2008-11-05 William Jon McCann - - * src/gvc-applet.c (popup_dock), (on_bar_is_muted_notify), - (gvc_applet_init): - * src/gvc-channel-bar.c (on_zero_adjustment_value_changed), - (update_mute_button), (gvc_channel_bar_init): - Fix initial mute setting for applet. When mute button - is not displayed use the "zero" position to indicate - muted state. - -2008-11-05 William Jon McCann - - * src/gvc-applet.c (gvc_applet_init): - * src/gvc-channel-bar.c (_scale_box_new), - (gvc_channel_bar_set_orientation), (update_mute_button), - (gvc_channel_bar_set_show_mute), (gvc_channel_bar_get_show_mute), - (gvc_channel_bar_set_property), (gvc_channel_bar_get_property), - (gvc_channel_bar_constructor), (gvc_channel_bar_class_init), - (gvc_channel_bar_init): - * src/gvc-channel-bar.h: - * src/gvc-mixer-dialog.c (add_stream): - Hide mute in vertical mode. Add icons to hint at scale direction - in horizontal mode. Fix orientation changing. - -2008-11-05 William Jon McCann - - * src/Makefile.am: - * src/gvc-mixer-control.c (gvc_mixer_control_get_default_source), - (gvc_mixer_control_get_sources), (remove_stream), (add_stream), - (update_sink), (update_source), (update_sink_input), - (update_event_role_stream), (remove_sink), (remove_source), - (remove_sink_input), (remove_source_output): - * src/gvc-mixer-control.h: - * src/gvc-mixer-dialog.c (add_stream), - (gvc_mixer_dialog_constructor): - * src/gvc-mixer-source.c (gvc_mixer_source_change_volume), - (gvc_mixer_source_change_is_muted), (gvc_mixer_source_constructor), - (gvc_mixer_source_class_init), (gvc_mixer_source_init), - (gvc_mixer_source_finalize), (gvc_mixer_source_new): - * src/gvc-mixer-source.h: - Add support for input streams. Only show default input and - output stream. - -2008-11-05 William Jon McCann - - * src/gvc-applet.c (gvc_applet_dispose), (on_control_ready): - * src/gvc-channel-bar.c (gvc_channel_bar_finalize): - * src/gvc-mixer-control.c (gvc_mixer_control_get_event_sink_input), - (gvc_mixer_control_get_default_sink), - (gvc_mixer_control_lookup_stream_id), (update_sink), - (update_sink_input), (update_event_role_stream), - (gvc_mixer_control_dispose), (gvc_mixer_control_init): - * src/gvc-mixer-control.h: - * src/gvc-mixer-dialog.c (on_control_stream_added), - (on_control_stream_removed), (gvc_mixer_dialog_constructor), - (gvc_mixer_dialog_dispose), (gvc_mixer_dialog_class_init): - * src/gvc-mixer-stream.c (gvc_mixer_stream_finalize): - Fix leaks. Use ids instead of indexes when possible. - Add method to lookup stream by id. - -2008-11-04 Marc-André Lureau - - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_constructor): Compile - with gtk+ <= 2.14. - -2008-11-03 William Jon McCann - - * src/gvc-applet.c (on_menu_activate_sounds), - (on_status_icon_popup_menu): - Add sound properties to menu. - -2008-11-03 William Jon McCann - - * src/gvc-applet.c (gvc_applet_init): - status-icon scroll events only available in gtk+ 2.15.0 - -2008-11-03 William Jon McCann - - * Makefile.am (SUBDIRS): - Add data dir. - -2008-11-03 William Jon McCann - - * data/Makefile.am: - * data/mate-volume-control-applet.desktop.in: - Install an autostart file for the volume applet - -2008-11-03 William Jon McCann - - * AUTHORS: - * Makefile.am: - * src/Makefile.am: - * src/applet-main.c (on_bus_name_lost), (acquire_name_on_proxy), - (acquire_name), (main): - * src/dialog-main.c (on_bus_name_lost), (acquire_name_on_proxy), - (acquire_name), (on_dialog_response), (on_control_ready), (main): - * src/gvc-applet.c (maybe_show_status_icon), (gvc_applet_start), - (gvc_applet_dispose), (gvc_applet_constructor), - (gvc_applet_class_init), (on_adjustment_value_changed), - (popup_dock), (on_status_icon_activate), - (on_menu_activate_open_volume_control), - (on_status_icon_popup_menu), (on_status_icon_scroll_event), - (gvc_applet_release_grab), (on_dock_button_press), (popdown_dock), - (gvc_applet_grab_notify), (on_dock_grab_notify), - (on_dock_grab_broken_event), (on_dock_key_release), (update_icon), - (on_stream_volume_notify), (on_stream_is_muted_notify), - (on_control_ready), (on_bar_is_muted_notify), (gvc_applet_init), - (gvc_applet_finalize), (gvc_applet_new): - * src/gvc-applet.h: - * src/gvc-channel-bar.c (_scale_box_new), (update_image), - (update_label), (gvc_channel_bar_set_name), - (gvc_channel_bar_set_icon_name), (gvc_channel_bar_set_orientation), - (gvc_channel_bar_set_adjustment), (gvc_channel_bar_get_adjustment), - (update_mute_button), (gvc_channel_bar_set_is_muted), - (gvc_channel_bar_get_is_muted), (gvc_channel_bar_set_property), - (gvc_channel_bar_get_property), (gvc_channel_bar_constructor), - (gvc_channel_bar_class_init), (on_mute_button_toggled), - (gvc_channel_bar_init), (gvc_channel_bar_finalize), - (gvc_channel_bar_new): - * src/gvc-channel-bar.h: - * src/gvc-mixer-control.c (gvc_mixer_control_get_event_sink_input), - (gvc_mixer_control_get_default_sink), (listify_hash_values_hfunc), - (gvc_stream_collate), (gvc_mixer_control_get_sinks), - (gvc_mixer_control_get_sink_inputs), (dec_outstanding), - (gvc_mixer_control_is_ready), (update_server), (update_sink), - (update_source), (set_icon_name_from_proplist), - (update_sink_input), (update_source_output), (update_client), - (_pa_context_get_sink_info_cb), (_pa_context_get_source_info_cb), - (_pa_context_get_sink_input_info_cb), - (_pa_context_get_source_output_info_cb), - (_pa_context_get_client_info_cb), (_pa_context_get_server_info_cb), - (remove_event_role_stream), (update_event_role_stream), - (_pa_ext_stream_restore_read_cb), - (_pa_ext_stream_restore_subscribe_cb), (req_update_server_info), - (req_update_client_info), (req_update_sink_info), - (req_update_source_info), (req_update_sink_input_info), - (req_update_source_output_info), (remove_client), (remove_sink), - (remove_source), (remove_sink_input), (remove_source_output), - (_pa_context_subscribe_cb), (gvc_mixer_control_ready), - (_pa_context_state_cb), (gvc_mixer_control_open), - (gvc_mixer_control_close), (gvc_mixer_control_dispose), - (gvc_mixer_control_constructor), (gvc_mixer_control_class_init), - (gvc_mixer_control_init), (gvc_mixer_control_finalize), - (gvc_mixer_control_new): - * src/gvc-mixer-control.h: - * src/gvc-mixer-dialog.c (gvc_mixer_dialog_set_mixer_control), - (gvc_mixer_dialog_get_mixer_control), - (gvc_mixer_dialog_set_property), (gvc_mixer_dialog_get_property), - (on_adjustment_value_changed), (on_bar_is_muted_notify), - (lookup_bar_for_stream), (on_stream_volume_notify), - (on_stream_is_muted_notify), (save_bar_for_stream), (add_stream), - (gvc_mixer_dialog_constructor), (gvc_mixer_dialog_class_init), - (gvc_mixer_dialog_init), (gvc_mixer_dialog_finalize), - (gvc_mixer_dialog_new): - * src/gvc-mixer-dialog.h: - * src/gvc-mixer-sink-input.c (gvc_mixer_sink_input_change_volume), - (gvc_mixer_sink_input_change_is_muted), - (gvc_mixer_sink_input_constructor), - (gvc_mixer_sink_input_class_init), (gvc_mixer_sink_input_init), - (gvc_mixer_sink_input_finalize), (gvc_mixer_sink_input_new): - * src/gvc-mixer-sink-input.h: - * src/gvc-mixer-sink.c (gvc_mixer_sink_change_volume), - (gvc_mixer_sink_change_is_muted), (gvc_mixer_sink_constructor), - (gvc_mixer_sink_class_init), (gvc_mixer_sink_init), - (gvc_mixer_sink_finalize), (gvc_mixer_sink_new): - * src/gvc-mixer-sink.h: - * src/gvc-mixer-stream.c (get_next_stream_serial), - (gvc_mixer_stream_get_pa_context), (gvc_mixer_stream_get_index), - (gvc_mixer_stream_get_id), (gvc_mixer_stream_get_num_channels), - (gvc_mixer_stream_get_volume), (gvc_mixer_stream_set_volume), - (gvc_mixer_stream_get_is_muted), (gvc_mixer_stream_get_is_default), - (gvc_mixer_stream_set_is_muted), (gvc_mixer_stream_set_is_default), - (gvc_mixer_stream_get_name), (gvc_mixer_stream_set_name), - (gvc_mixer_stream_get_icon_name), (gvc_mixer_stream_set_icon_name), - (gvc_mixer_stream_set_property), (gvc_mixer_stream_get_property), - (gvc_mixer_stream_constructor), - (gvc_mixer_stream_real_change_volume), - (gvc_mixer_stream_real_change_is_muted), - (gvc_mixer_stream_change_volume), - (gvc_mixer_stream_change_is_muted), (gvc_mixer_stream_class_init), - (gvc_mixer_stream_init), (gvc_mixer_stream_finalize): - * src/gvc-mixer-stream.h: - Initial commit. - diff --git a/mate-volume-control/Makefile.am b/mate-volume-control/Makefile.am index 0c3fa4c..0814356 100644 --- a/mate-volume-control/Makefile.am +++ b/mate-volume-control/Makefile.am @@ -1,16 +1,78 @@ NULL = -SUBDIRS = \ - data \ - src \ +bin_PROGRAMS = \ + mate-volume-control-applet \ + mate-volume-control \ $(NULL) -EXTRA_DIST = \ - ChangeLog.pre-2-26 \ +AM_CPPFLAGS = \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/sound-theme \ + $(VOLUME_CONTROL_CFLAGS) \ + $(DISABLE_DEPRECATED) \ + -DLOCALE_DIR=\""$(datadir)/locale"\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DGLADEDIR=\""$(pkgdatadir)"\" \ + -DICON_DATA_DIR="\"$(pkgdatadir)/icons\"" \ + -DSOUND_DATA_DIR="\"$(datadir)/sounds\"" \ + -DSOUND_SET_DIR="\"$(pkgdatadir)/sounds\"" \ $(NULL) -MAINTAINERCLEANFILES = \ - *~ \ +noinst_LTLIBRARIES = libmatevolumecontrol.la +libmatevolumecontrol_la_SOURCES = \ + gvc-channel-bar.h \ + gvc-channel-bar.c \ + gvc-utils.c \ + gvc-utils.h \ $(NULL) +mate_volume_control_applet_LDADD = \ + -lm \ + libmatevolumecontrol.la \ + $(VOLUME_CONTROL_LIBS) \ + $(NULL) + +mate_volume_control_applet_SOURCES = \ + gvc-stream-status-icon.h \ + gvc-stream-status-icon.c \ + gvc-applet.h \ + gvc-applet.c \ + applet-main.c \ + $(NULL) + +mate_volume_control_LDADD = \ + -lm \ + libmatevolumecontrol.la \ + $(VOLUME_CONTROL_LIBS) \ + $(NULL) + +mate_volume_control_SOURCES = \ + gvc-balance-bar.h \ + gvc-balance-bar.c \ + gvc-level-bar.h \ + gvc-level-bar.c \ + gvc-combo-box.h \ + gvc-combo-box.c \ + gvc-sound-theme-chooser.c \ + gvc-sound-theme-chooser.h \ + gvc-speaker-test.h \ + gvc-speaker-test.c \ + sound-theme-file-utils.c \ + sound-theme-file-utils.h \ + gvc-mixer-dialog.c \ + gvc-mixer-dialog.h \ + dialog-main.c \ + $(NULL) + +BUILT_SOURCES = \ + $(NULL) + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in + -include $(top_srcdir)/git.mk diff --git a/mate-volume-control/applet-main.c b/mate-volume-control/applet-main.c new file mode 100644 index 0000000..1b4bb0a --- /dev/null +++ b/mate-volume-control/applet-main.c @@ -0,0 +1,94 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "gvc-applet.h" + +static gboolean show_version = FALSE; +static gboolean debug = FALSE; + +int +main (int argc, char **argv) +{ + GError *error = NULL; + GvcApplet *applet; + UniqueApp *app; + GOptionEntry entries[] = { + { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, + { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, N_("Enable debug"), NULL }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init_with_args (&argc, &argv, + _(" — MATE Volume Control Applet"), + entries, GETTEXT_PACKAGE, + &error); + + if (error != NULL) { + g_warning ("%s", error->message); + return 1; + } + if (show_version == TRUE) { + g_print ("%s %s\n", argv[0], VERSION); + return 0; + } + if (debug == TRUE) { + g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); + } + + app = unique_app_new (GVC_APPLET_DBUS_NAME, NULL); + + if (unique_app_is_running (app) == TRUE) { + g_warning ("Applet is already running, exiting"); + return 0; + } + if (mate_mixer_init () == FALSE) { + g_warning ("libmatemixer initialization failed, exiting"); + return 1; + } + + gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), + ICON_DATA_DIR); + + applet = gvc_applet_new (); + + gvc_applet_start (applet); + gtk_main (); + + g_object_unref (applet); + g_object_unref (app); + + return 0; +} diff --git a/mate-volume-control/data/Makefile.am b/mate-volume-control/data/Makefile.am deleted file mode 100644 index c580a45..0000000 --- a/mate-volume-control/data/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -NULL = - -SUBDIRS = \ - icons \ - $(NULL) - -@INTLTOOL_DESKTOP_RULE@ -autostartdir = $(sysconfdir)/xdg/autostart -autostart_in_files = mate-volume-control-applet.desktop.in -autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) - -appsdir = $(datadir)/applications -apps_in_files = mate-volume-control.desktop.in -apps_DATA = $(apps_in_files:.desktop.in=.desktop) - -EXTRA_DIST = \ - $(autostart_in_files) \ - mate-volume-control.desktop.in.in \ - $(NULL) - -CLEANFILES = \ - mate-volume-control.desktop \ - $(NULL) - -DISTCLEANFILES = \ - mate-volume-control-applet.desktop \ - $(NULL) - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/16x16/Makefile.am b/mate-volume-control/data/icons/16x16/Makefile.am deleted file mode 100644 index 05a9700..0000000 --- a/mate-volume-control/data/icons/16x16/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIRS = status - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/16x16/status/Makefile.am b/mate-volume-control/data/icons/16x16/status/Makefile.am deleted file mode 100644 index 041fb3b..0000000 --- a/mate-volume-control/data/icons/16x16/status/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -NULL = - -themedir = $(pkgdatadir)/icons/mate -size = 16x16 -context = status - -iconsdir = $(themedir)/$(size)/$(context) - -icons_DATA = \ - audio-input-microphone-high.png \ - audio-input-microphone-low.png \ - audio-input-microphone-medium.png \ - audio-input-microphone-muted.png \ - $(NULL) - -EXTRA_DIST = \ - $(icons_DATA) \ - $(NULL) - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-high.png b/mate-volume-control/data/icons/16x16/status/audio-input-microphone-high.png deleted file mode 100644 index 160d2b8..0000000 Binary files a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-high.png and /dev/null differ diff --git a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-low.png b/mate-volume-control/data/icons/16x16/status/audio-input-microphone-low.png deleted file mode 100644 index b08ab64..0000000 Binary files a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-low.png and /dev/null differ diff --git a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-medium.png b/mate-volume-control/data/icons/16x16/status/audio-input-microphone-medium.png deleted file mode 100644 index 48a4c05..0000000 Binary files a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-medium.png and /dev/null differ diff --git a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-muted.png b/mate-volume-control/data/icons/16x16/status/audio-input-microphone-muted.png deleted file mode 100644 index 7aed52c..0000000 Binary files a/mate-volume-control/data/icons/16x16/status/audio-input-microphone-muted.png and /dev/null differ diff --git a/mate-volume-control/data/icons/22x22/Makefile.am b/mate-volume-control/data/icons/22x22/Makefile.am deleted file mode 100644 index 05a9700..0000000 --- a/mate-volume-control/data/icons/22x22/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIRS = status - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/22x22/status/Makefile.am b/mate-volume-control/data/icons/22x22/status/Makefile.am deleted file mode 100644 index 53b0a3a..0000000 --- a/mate-volume-control/data/icons/22x22/status/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -NULL = - -themedir = $(pkgdatadir)/icons/mate -size = 22x22 -context = status - -iconsdir = $(themedir)/$(size)/$(context) - -icons_DATA = \ - audio-input-microphone-high.png \ - audio-input-microphone-low.png \ - audio-input-microphone-medium.png \ - audio-input-microphone-muted.png \ - $(NULL) - -EXTRA_DIST = \ - $(icons_DATA) \ - $(NULL) - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-high.png b/mate-volume-control/data/icons/22x22/status/audio-input-microphone-high.png deleted file mode 100644 index ca3770f..0000000 Binary files a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-high.png and /dev/null differ diff --git a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-low.png b/mate-volume-control/data/icons/22x22/status/audio-input-microphone-low.png deleted file mode 100644 index 75d0612..0000000 Binary files a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-low.png and /dev/null differ diff --git a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-medium.png b/mate-volume-control/data/icons/22x22/status/audio-input-microphone-medium.png deleted file mode 100644 index c1efeb5..0000000 Binary files a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-medium.png and /dev/null differ diff --git a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-muted.png b/mate-volume-control/data/icons/22x22/status/audio-input-microphone-muted.png deleted file mode 100644 index 9f3dd3c..0000000 Binary files a/mate-volume-control/data/icons/22x22/status/audio-input-microphone-muted.png and /dev/null differ diff --git a/mate-volume-control/data/icons/24x24/Makefile.am b/mate-volume-control/data/icons/24x24/Makefile.am deleted file mode 100644 index 05a9700..0000000 --- a/mate-volume-control/data/icons/24x24/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIRS = status - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/24x24/status/Makefile.am b/mate-volume-control/data/icons/24x24/status/Makefile.am deleted file mode 100644 index 9ea93cd..0000000 --- a/mate-volume-control/data/icons/24x24/status/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -NULL = - -themedir = $(pkgdatadir)/icons/mate -size = 24x24 -context = status - -iconsdir = $(themedir)/$(size)/$(context) - -icons_DATA = \ - audio-input-microphone-high.png \ - audio-input-microphone-low.png \ - audio-input-microphone-medium.png \ - audio-input-microphone-muted.png \ - $(NULL) - -EXTRA_DIST = \ - $(icons_DATA) \ - $(NULL) - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-high.png b/mate-volume-control/data/icons/24x24/status/audio-input-microphone-high.png deleted file mode 100644 index fe10ab2..0000000 Binary files a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-high.png and /dev/null differ diff --git a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-low.png b/mate-volume-control/data/icons/24x24/status/audio-input-microphone-low.png deleted file mode 100644 index ee180d8..0000000 Binary files a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-low.png and /dev/null differ diff --git a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-medium.png b/mate-volume-control/data/icons/24x24/status/audio-input-microphone-medium.png deleted file mode 100644 index eb6aa28..0000000 Binary files a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-medium.png and /dev/null differ diff --git a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-muted.png b/mate-volume-control/data/icons/24x24/status/audio-input-microphone-muted.png deleted file mode 100644 index b969738..0000000 Binary files a/mate-volume-control/data/icons/24x24/status/audio-input-microphone-muted.png and /dev/null differ diff --git a/mate-volume-control/data/icons/32x32/Makefile.am b/mate-volume-control/data/icons/32x32/Makefile.am deleted file mode 100644 index 05a9700..0000000 --- a/mate-volume-control/data/icons/32x32/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIRS = status - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/32x32/status/Makefile.am b/mate-volume-control/data/icons/32x32/status/Makefile.am deleted file mode 100644 index 68afbb7..0000000 --- a/mate-volume-control/data/icons/32x32/status/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -NULL = - -themedir = $(pkgdatadir)/icons/mate -size = 32x32 -context = status - -iconsdir = $(themedir)/$(size)/$(context) - -icons_DATA = \ - audio-input-microphone-high.png \ - audio-input-microphone-low.png \ - audio-input-microphone-medium.png \ - audio-input-microphone-muted.png \ - $(NULL) - -EXTRA_DIST = \ - $(icons_DATA) \ - $(NULL) - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-high.png b/mate-volume-control/data/icons/32x32/status/audio-input-microphone-high.png deleted file mode 100644 index 8bd9d5b..0000000 Binary files a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-high.png and /dev/null differ diff --git a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-low.png b/mate-volume-control/data/icons/32x32/status/audio-input-microphone-low.png deleted file mode 100644 index 8a09c9f..0000000 Binary files a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-low.png and /dev/null differ diff --git a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-medium.png b/mate-volume-control/data/icons/32x32/status/audio-input-microphone-medium.png deleted file mode 100644 index a45c79c..0000000 Binary files a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-medium.png and /dev/null differ diff --git a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-muted.png b/mate-volume-control/data/icons/32x32/status/audio-input-microphone-muted.png deleted file mode 100644 index 8161f1f..0000000 Binary files a/mate-volume-control/data/icons/32x32/status/audio-input-microphone-muted.png and /dev/null differ diff --git a/mate-volume-control/data/icons/Makefile.am b/mate-volume-control/data/icons/Makefile.am deleted file mode 100644 index 67d9cea..0000000 --- a/mate-volume-control/data/icons/Makefile.am +++ /dev/null @@ -1,25 +0,0 @@ -NULL = - -SUBDIRS = \ - scalable \ - 16x16 \ - 22x22 \ - 24x24 \ - 32x32 \ - $(NULL) - -gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/mate - -install-data-hook: update-icon-cache -uninstall-hook: update-icon-cache -update-icon-cache: - @-if test -z "$(DESTDIR)"; then \ - echo "Updating Gtk icon cache."; \ - $(gtk_update_icon_cache); \ - else \ - echo "*** Icon cache not updated. After (un)install, run this:"; \ - echo "*** $(gtk_update_icon_cache)"; \ - fi - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/scalable/Makefile.am b/mate-volume-control/data/icons/scalable/Makefile.am deleted file mode 100644 index 2cf31cf..0000000 --- a/mate-volume-control/data/icons/scalable/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIRS = status devices - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/scalable/devices/Makefile.am b/mate-volume-control/data/icons/scalable/devices/Makefile.am deleted file mode 100644 index e22cb77..0000000 --- a/mate-volume-control/data/icons/scalable/devices/Makefile.am +++ /dev/null @@ -1,35 +0,0 @@ -NULL = - -themedir = $(pkgdatadir)/icons/mate -size = 48x48 -context = devices - -iconsdir = $(themedir)/$(size)/$(context) - -icons_DATA = \ - audio-speaker-center.svg \ - audio-speaker-center-testing.svg \ - audio-speaker-left-back.svg \ - audio-speaker-left-back-testing.svg \ - audio-speaker-left.svg \ - audio-speaker-left-side.svg \ - audio-speaker-left-side-testing.svg \ - audio-speaker-left-testing.svg \ - audio-speaker-right-back.svg \ - audio-speaker-right-back-testing.svg \ - audio-speaker-right.svg \ - audio-speaker-right-side.svg \ - audio-speaker-right-side-testing.svg \ - audio-speaker-right-testing.svg \ - audio-speaker-center-back-testing.svg \ - audio-speaker-center-back.svg \ - audio-subwoofer.svg \ - audio-subwoofer-testing.svg \ - $(NULL) - -EXTRA_DIST = \ - $(icons_DATA) \ - $(NULL) - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back-testing.svg deleted file mode 100644 index 93afe0b..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back-testing.svg +++ /dev/null @@ -1,539 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - center - testing - highlighted - - - audio-speaker-center-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back.svg deleted file mode 100644 index 7d278f8..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-back.svg +++ /dev/null @@ -1,506 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - center - - - audio-speaker-center - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-testing.svg deleted file mode 100644 index 09f40bc..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center-testing.svg +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - center - testing - highlighted - - - audio-speaker-center-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-center.svg deleted file mode 100644 index 886b4b8..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-center.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - center - - - audio-speaker-center - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back-testing.svg deleted file mode 100644 index e1618fc..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back-testing.svg +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - left-back - testing - highlighted - - - audio-speaker-left-b-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back.svg deleted file mode 100644 index d7a6f43..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-back.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - left-back - - - audio-speaker-left-back - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side-testing.svg deleted file mode 100644 index ad0f430..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side-testing.svg +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - left-side - testing - highlighted - - - audio-speaker-left-side-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side.svg deleted file mode 100644 index 4851454..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-side.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - left-side - - - audio-speaker-left-side - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-testing.svg deleted file mode 100644 index 7b9316f..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left-testing.svg +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - left - testing - highlighted - - - audio-speaker-left-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-left.svg deleted file mode 100644 index 1ee3869..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-left.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - left - - - audio-speaker-left - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back-testing.svg deleted file mode 100644 index 0f633a9..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back-testing.svg +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - right-back - testing - highlighted - - - audio-speaker-right-back-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back.svg deleted file mode 100644 index ba1dead..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-back.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - right-back - - - audio-speaker-right-back - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side-testing.svg deleted file mode 100644 index 577dd86..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side-testing.svg +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - right-side - testing - highlighted - - - audio-speaker-right-side-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side.svg deleted file mode 100644 index be6a746..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-side.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - right-side - - - audio-speaker-right-side - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-testing.svg deleted file mode 100644 index 3a4e903..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right-testing.svg +++ /dev/null @@ -1,913 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - right - testing - highlighted - - - audio-speaker-right-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right.svg b/mate-volume-control/data/icons/scalable/devices/audio-speaker-right.svg deleted file mode 100644 index bde2e35..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-speaker-right.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - speaker - output - right - - - audio-speaker-right - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-subwoofer-testing.svg b/mate-volume-control/data/icons/scalable/devices/audio-subwoofer-testing.svg deleted file mode 100644 index 6ae74ba..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-subwoofer-testing.svg +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - audio - device - subwoofer - output - testing - highlighted - - - audio-subwoofer-testing - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/devices/audio-subwoofer.svg b/mate-volume-control/data/icons/scalable/devices/audio-subwoofer.svg deleted file mode 100644 index dbed916..0000000 --- a/mate-volume-control/data/icons/scalable/devices/audio-subwoofer.svg +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - audio - device - subwoofer - output - - - audio-subwoofer - - - Evangeline McGlynn - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/status/Makefile.am b/mate-volume-control/data/icons/scalable/status/Makefile.am deleted file mode 100644 index c42c14e..0000000 --- a/mate-volume-control/data/icons/scalable/status/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -NULL = - -themedir = $(pkgdatadir)/icons/mate -size = scalable -context = status - -iconsdir = $(themedir)/$(size)/$(context) - -icons_DATA = \ - audio-input-microphone-high.svg \ - audio-input-microphone-low.svg \ - audio-input-microphone-medium.svg \ - audio-input-microphone-muted.svg \ - $(NULL) - -EXTRA_DIST = \ - $(icons_DATA) \ - $(NULL) - - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-high.svg b/mate-volume-control/data/icons/scalable/status/audio-input-microphone-high.svg deleted file mode 100644 index fa30895..0000000 --- a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-high.svg +++ /dev/null @@ -1,1076 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-low.svg b/mate-volume-control/data/icons/scalable/status/audio-input-microphone-low.svg deleted file mode 100644 index 2aec722..0000000 --- a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-low.svg +++ /dev/null @@ -1,1045 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-medium.svg b/mate-volume-control/data/icons/scalable/status/audio-input-microphone-medium.svg deleted file mode 100644 index ba8b5a4..0000000 --- a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-medium.svg +++ /dev/null @@ -1,1066 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-muted.svg b/mate-volume-control/data/icons/scalable/status/audio-input-microphone-muted.svg deleted file mode 100644 index 962b4de..0000000 --- a/mate-volume-control/data/icons/scalable/status/audio-input-microphone-muted.svg +++ /dev/null @@ -1,1040 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mate-volume-control/data/mate-volume-control-applet.desktop.in b/mate-volume-control/data/mate-volume-control-applet.desktop.in deleted file mode 100644 index 8d4208f..0000000 --- a/mate-volume-control/data/mate-volume-control-applet.desktop.in +++ /dev/null @@ -1,17 +0,0 @@ -[Desktop Entry] -_Name=Volume Control -_Comment=Show desktop volume control -Icon=multimedia-volume-control -Exec=mate-volume-control-applet -Terminal=false -Type=Application -Categories=AudioVideo;Mixer;Settings;HardwareSettings; -Keywords=MATE;volume;control;mixer;settings;sound; -NoDisplay=true -OnlyShowIn=MATE; -X-MATE-Bugzilla-Bugzilla=MATE -X-MATE-Bugzilla-Product=mate-media -X-MATE-Bugzilla-Component=mate-volume-control -# See http://bugzilla.mate.org/show_bug.cgi?id=568320 -#X-MATE-Autostart-Phase=Panel -X-MATE-Autostart-Notify=true diff --git a/mate-volume-control/data/mate-volume-control.desktop.in.in b/mate-volume-control/data/mate-volume-control.desktop.in.in deleted file mode 100644 index 60708a4..0000000 --- a/mate-volume-control/data/mate-volume-control.desktop.in.in +++ /dev/null @@ -1,15 +0,0 @@ -[Desktop Entry] -_Name=Sound -_Comment=Change sound volume and sound events -Exec=mate-volume-control -Icon=multimedia-volume-control -Terminal=false -Type=Application -StartupNotify=true -Categories=AudioVideo;Mixer;Settings;HardwareSettings; -Keywords=MATE;volume;control;mixer;settings;sound;events; -OnlyShowIn=MATE; -X-MATE-Bugzilla-Bugzilla=MATE -X-MATE-Bugzilla-Product=mate-media -X-MATE-Bugzilla-Component=mate-volume-control -X-MATE-Bugzilla-Version=@VERSION@ diff --git a/mate-volume-control/dialog-main.c b/mate-volume-control/dialog-main.c new file mode 100644 index 0000000..08c55fc --- /dev/null +++ b/mate-volume-control/dialog-main.c @@ -0,0 +1,274 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include + +#include "gvc-mixer-dialog.h" + +#define DIALOG_POPUP_TIMEOUT 3 + +static guint popup_id = 0; +static gboolean debug = FALSE; +static gboolean show_version = FALSE; + +static gchar *page = NULL; +static GtkWidget *app_dialog = NULL; +static GtkWidget *warning_dialog = NULL; + +static void +on_dialog_response (GtkDialog *dialog, guint response_id, gpointer data) +{ + gboolean destroy = GPOINTER_TO_INT (data); + + if (destroy == TRUE) + gtk_widget_destroy (GTK_WIDGET (dialog)); + + gtk_main_quit (); +} + +static void +on_dialog_close (GtkDialog *dialog, gpointer data) +{ + gboolean destroy = GPOINTER_TO_INT (data); + + if (destroy == TRUE) + gtk_widget_destroy (GTK_WIDGET (dialog)); + + gtk_main_quit (); +} + +static UniqueResponse +on_app_message_received (UniqueApp *app, + int command, + UniqueMessageData *message_data, + guint time_, + gpointer user_data) +{ + gtk_window_present (GTK_WINDOW (user_data)); + + return UNIQUE_RESPONSE_OK; +} + +static void +remove_warning_dialog (void) +{ + if (popup_id != 0) { + g_source_remove (popup_id); + popup_id = 0; + } + + g_clear_pointer (&warning_dialog, gtk_widget_destroy); +} + +static void +context_ready (MateMixerContext *context, UniqueApp *app) +{ + /* The dialog might be already created, e.g. when reconnected + * to a sound server */ + if (app_dialog != NULL) + return; + + app_dialog = GTK_WIDGET (gvc_mixer_dialog_new (context)); + + g_signal_connect (G_OBJECT (app_dialog), + "response", + G_CALLBACK (on_dialog_response), + GINT_TO_POINTER (FALSE)); + g_signal_connect (G_OBJECT (app_dialog), + "close", + G_CALLBACK (on_dialog_close), + GINT_TO_POINTER (FALSE)); + + gvc_mixer_dialog_set_page (GVC_MIXER_DIALOG (app_dialog), page); + + g_signal_connect (G_OBJECT (app), + "message-received", + G_CALLBACK (on_app_message_received), + app_dialog); + + gtk_widget_show (app_dialog); +} + +static void +on_context_state_notify (MateMixerContext *context, + GParamSpec *pspec, + UniqueApp *app) +{ + MateMixerState state = mate_mixer_context_get_state (context); + + if (state == MATE_MIXER_STATE_READY) { + remove_warning_dialog (); + context_ready (context, app); + } + else if (state == MATE_MIXER_STATE_FAILED) { + GtkWidget *dialog; + + remove_warning_dialog (); + + dialog = gtk_message_dialog_new (GTK_WINDOW (app_dialog), + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Sound system is not available")); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (on_dialog_response), + GINT_TO_POINTER (TRUE)); + g_signal_connect (G_OBJECT (dialog), + "close", + G_CALLBACK (on_dialog_close), + GINT_TO_POINTER (TRUE)); + + gtk_widget_show (dialog); + } +} + +static gboolean +dialog_popup_timeout (gpointer data) +{ + warning_dialog = gtk_message_dialog_new (GTK_WINDOW (app_dialog), + 0, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CANCEL, + _("Waiting for sound system to respond")); + + g_signal_connect (G_OBJECT (warning_dialog), + "response", + G_CALLBACK (on_dialog_response), + GINT_TO_POINTER (TRUE)); + g_signal_connect (G_OBJECT (warning_dialog), + "close", + G_CALLBACK (on_dialog_close), + GINT_TO_POINTER (TRUE)); + + gtk_widget_show (warning_dialog); + + return FALSE; +} + +int +main (int argc, char **argv) +{ + GError *error = NULL; + gchar *backend = NULL; + MateMixerContext *context; + UniqueApp *app; + GOptionEntry entries[] = { + { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, N_("Sound system backend"), "pulse|alsa|oss|null" }, + { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, N_("Enable debug"), NULL }, + { "page", 'p', 0, G_OPTION_ARG_STRING, &page, N_("Startup page"), "effects|hardware|input|output|applications" }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init_with_args (&argc, &argv, + _(" — MATE Volume Control"), + entries, GETTEXT_PACKAGE, + &error); + + if (error != NULL) { + g_warning ("%s", error->message); + return 1; + } + if (show_version == TRUE) { + g_print ("%s %s\n", argv[0], VERSION); + return 0; + } + if (debug == TRUE) { + g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); + } + + app = unique_app_new (GVC_DIALOG_DBUS_NAME, NULL); + + if (unique_app_is_running (app) == TRUE) { + unique_app_send_message (app, UNIQUE_ACTIVATE, NULL); + return 0; + } + if (mate_mixer_init () == FALSE) { + g_warning ("libmatemixer initialization failed, exiting"); + return 1; + } + + context = mate_mixer_context_new (); + + if (backend != NULL) { + if (strcmp (backend, "pulse") == 0) + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_PULSEAUDIO); + else if (strcmp (backend, "alsa") == 0) + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_ALSA); + else if (strcmp (backend, "oss") == 0) + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_OSS); + else if (strcmp (backend, "null") == 0) + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_NULL); + else { + g_warning ("Invalid backend: %s", backend); + g_object_unref (context); + g_object_unref (app); + g_free (backend); + return 1; + } + + g_free (backend); + } + + mate_mixer_context_set_app_name (context, _("Volume Control")); + mate_mixer_context_set_app_id (context, GVC_DIALOG_DBUS_NAME); + mate_mixer_context_set_app_version (context, VERSION); + mate_mixer_context_set_app_icon (context, "multimedia-volume-control"); + + g_signal_connect (G_OBJECT (context), + "notify::state", + G_CALLBACK (on_context_state_notify), + app); + + mate_mixer_context_open (context); + + if (mate_mixer_context_get_state (context) == MATE_MIXER_STATE_CONNECTING) { + popup_id = g_timeout_add_seconds (DIALOG_POPUP_TIMEOUT, + dialog_popup_timeout, + NULL); + } + + gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), + ICON_DATA_DIR); + + gtk_window_set_default_icon_name ("multimedia-volume-control"); + + gtk_main (); + + g_object_unref (context); + g_object_unref (app); + + return 0; +} diff --git a/mate-volume-control/gvc-applet.c b/mate-volume-control/gvc-applet.c new file mode 100644 index 0000000..3cb691f --- /dev/null +++ b/mate-volume-control/gvc-applet.c @@ -0,0 +1,357 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include + +#include "gvc-applet.h" +#include "gvc-stream-status-icon.h" + +#define GVC_APPLET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_APPLET, GvcAppletPrivate)) + +static const gchar *icon_names_output[] = { + "audio-volume-muted", + "audio-volume-low", + "audio-volume-medium", + "audio-volume-high", + NULL +}; + +static const gchar *icon_names_input[] = { + "audio-input-microphone-muted", + "audio-input-microphone-low", + "audio-input-microphone-medium", + "audio-input-microphone-high", + NULL +}; + +struct _GvcAppletPrivate +{ + GvcStreamStatusIcon *icon_input; + GvcStreamStatusIcon *icon_output; + gboolean running; + MateMixerContext *context; + MateMixerStream *input; +}; + +static void gvc_applet_class_init (GvcAppletClass *klass); +static void gvc_applet_init (GvcApplet *applet); + +G_DEFINE_TYPE (GvcApplet, gvc_applet, G_TYPE_OBJECT) + +static void +update_icon_input (GvcApplet *applet) +{ + MateMixerStreamControl *control = NULL; + gboolean show = FALSE; + + /* Enable the input icon in case there is an input stream present and there + * is a non-mixer application using the input */ + if (applet->priv->input != NULL) { + const gchar *app_id; + const GList *inputs = + mate_mixer_stream_list_controls (applet->priv->input); + + control = mate_mixer_stream_get_default_control (applet->priv->input); + + while (inputs != NULL) { + MateMixerStreamControl *input = + MATE_MIXER_STREAM_CONTROL (inputs->data); + MateMixerStreamControlRole role = + mate_mixer_stream_control_get_role (input); + + if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) { + MateMixerAppInfo *app_info = + mate_mixer_stream_control_get_app_info (input); + + app_id = mate_mixer_app_info_get_id (app_info); + if (app_id == NULL) { + /* A recording application which has no + * identifier set */ + g_debug ("Found a recording application control %s", + mate_mixer_stream_control_get_label (input)); + + if G_UNLIKELY (control == NULL) { + /* In the unlikely case when there is no + * default input control, use the application + * control for the icon */ + control = input; + } + show = TRUE; + break; + } + + if (strcmp (app_id, "org.mate.VolumeControl") != 0 && + strcmp (app_id, "org.gnome.VolumeControl") != 0 && + strcmp (app_id, "org.PulseAudio.pavucontrol") != 0) { + g_debug ("Found a recording application %s", app_id); + + if G_UNLIKELY (control == NULL) + control = input; + + show = TRUE; + break; + } + } + inputs = inputs->next; + } + + if (show == TRUE) + g_debug ("Input icon enabled"); + else + g_debug ("There is no recording application, input icon disabled"); + } + + gvc_stream_status_icon_set_control (applet->priv->icon_input, control); + + gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_input), show); +} + +static void +update_icon_output (GvcApplet *applet) +{ + MateMixerStream *stream; + MateMixerStreamControl *control = NULL; + + stream = mate_mixer_context_get_default_output_stream (applet->priv->context); + if (stream != NULL) + control = mate_mixer_stream_get_default_control (stream); + + gvc_stream_status_icon_set_control (applet->priv->icon_output, control); + + if (control != NULL) { + g_debug ("Output icon enabled"); + gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), + TRUE); + } + else { + g_debug ("There is no output stream/control, output icon disabled"); + gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), + FALSE); + } +} + +static void +on_input_stream_control_added (MateMixerStream *stream, + const gchar *name, + GvcApplet *applet) +{ + MateMixerStreamControl *control; + + control = mate_mixer_stream_get_control (stream, name); + if G_LIKELY (control != NULL) { + MateMixerStreamControlRole role = + mate_mixer_stream_control_get_role (control); + + /* Non-application input control doesn't affect the icon */ + if (role != MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) + return; + } + + /* Either an application control has been added or we couldn't + * read the control, this shouldn't happen but let's revalidate the + * icon to be sure if it does */ + update_icon_input (applet); +} + +static void +on_input_stream_control_removed (MateMixerStream *stream, + const gchar *name, + GvcApplet *applet) +{ + /* The removed stream could be an application input, which may cause + * the input status icon to disappear */ + update_icon_input (applet); +} + +static gboolean +update_default_input_stream (GvcApplet *applet) +{ + MateMixerStream *stream; + + stream = mate_mixer_context_get_default_input_stream (applet->priv->context); + if (stream == applet->priv->input) + return FALSE; + + /* The input stream has changed */ + if (applet->priv->input != NULL) { + g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input), + applet); + g_object_unref (applet->priv->input); + } + + applet->priv->input = g_object_ref (stream); + if (applet->priv->input != NULL) { + g_signal_connect (G_OBJECT (applet->priv->input), + "control-added", + G_CALLBACK (on_input_stream_control_added), + applet); + g_signal_connect (G_OBJECT (applet->priv->input), + "control-removed", + G_CALLBACK (on_input_stream_control_removed), + applet); + } + + /* Return TRUE if the default input stream has changed */ + return TRUE; +} + +static void +on_context_state_notify (MateMixerContext *context, + GParamSpec *pspec, + GvcApplet *applet) +{ + MateMixerState state = mate_mixer_context_get_state (context); + + switch (state) { + case MATE_MIXER_STATE_FAILED: + g_warning ("Failed to connect to a sound system"); + break; + + case MATE_MIXER_STATE_READY: + update_default_input_stream (applet); + + /* Each status change may affect the visibility of the icons */ + update_icon_output (applet); + update_icon_input (applet); + break; + default: + break; + } +} + +static void +on_context_default_input_stream_notify (MateMixerContext *context, + GParamSpec *pspec, + GvcApplet *applet) +{ + if (update_default_input_stream (applet) == FALSE) + return; + + update_icon_input (applet); +} + +static void +on_context_default_output_stream_notify (MateMixerContext *control, + GParamSpec *pspec, + GvcApplet *applet) +{ + update_icon_output (applet); +} + +void +gvc_applet_start (GvcApplet *applet) +{ + g_return_if_fail (GVC_IS_APPLET (applet)); + + if G_UNLIKELY (applet->priv->running == TRUE) + return; + + if G_UNLIKELY (mate_mixer_context_open (applet->priv->context) == FALSE) { + /* Normally this should never happen, in the worst case we + * should end up with the Null module */ + g_warning ("Failed to connect to a sound system"); + } + + g_debug ("Applet has been started"); + + applet->priv->running = TRUE; +} + +static void +gvc_applet_dispose (GObject *object) +{ + GvcApplet *applet = GVC_APPLET (object); + + if (applet->priv->input != NULL) { + g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input), + applet); + g_clear_object (&applet->priv->input); + } + + g_clear_object (&applet->priv->context); + g_clear_object (&applet->priv->icon_input); + g_clear_object (&applet->priv->icon_output); + + G_OBJECT_CLASS (gvc_applet_parent_class)->dispose (object); +} + +static void +gvc_applet_class_init (GvcAppletClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gvc_applet_dispose; + + g_type_class_add_private (klass, sizeof (GvcAppletPrivate)); +} + +static void +gvc_applet_init (GvcApplet *applet) +{ + applet->priv = GVC_APPLET_GET_PRIVATE (applet); + + applet->priv->icon_input = gvc_stream_status_icon_new (NULL, icon_names_input); + applet->priv->icon_output = gvc_stream_status_icon_new (NULL, icon_names_output); + + gvc_stream_status_icon_set_display_name (applet->priv->icon_input, _("Input")); + gvc_stream_status_icon_set_display_name (applet->priv->icon_output, _("Output")); + + gtk_status_icon_set_title (GTK_STATUS_ICON (applet->priv->icon_input), + _("Microphone Volume")); + gtk_status_icon_set_title (GTK_STATUS_ICON (applet->priv->icon_output), + _("Sound Output Volume")); + + applet->priv->context = mate_mixer_context_new (); + + mate_mixer_context_set_app_name (applet->priv->context, + _("MATE Volume Control Applet")); + + mate_mixer_context_set_app_id (applet->priv->context, GVC_APPLET_DBUS_NAME); + mate_mixer_context_set_app_version (applet->priv->context, VERSION); + mate_mixer_context_set_app_icon (applet->priv->context, "multimedia-volume-control"); + + g_signal_connect (G_OBJECT (applet->priv->context), + "notify::state", + G_CALLBACK (on_context_state_notify), + applet); + g_signal_connect (G_OBJECT (applet->priv->context), + "notify::default-input-stream", + G_CALLBACK (on_context_default_input_stream_notify), + applet); + g_signal_connect (G_OBJECT (applet->priv->context), + "notify::default-output-stream", + G_CALLBACK (on_context_default_output_stream_notify), + applet); +} + +GvcApplet * +gvc_applet_new (void) +{ + return g_object_new (GVC_TYPE_APPLET, NULL); +} diff --git a/mate-volume-control/gvc-applet.h b/mate-volume-control/gvc-applet.h new file mode 100644 index 0000000..991ef6d --- /dev/null +++ b/mate-volume-control/gvc-applet.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_APPLET_H +#define __GVC_APPLET_H + +#include +#include + +G_BEGIN_DECLS + +#define GVC_APPLET_DBUS_NAME "org.mate.VolumeControlApplet" + +#define GVC_TYPE_APPLET (gvc_applet_get_type ()) +#define GVC_APPLET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_APPLET, GvcApplet)) +#define GVC_APPLET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GVC_TYPE_APPLET, GvcAppletClass)) +#define GVC_IS_APPLET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_APPLET)) +#define GVC_IS_APPLET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_APPLET)) +#define GVC_APPLET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_APPLET, GvcAppletClass)) + +typedef struct _GvcApplet GvcApplet; +typedef struct _GvcAppletClass GvcAppletClass; +typedef struct _GvcAppletPrivate GvcAppletPrivate; + +struct _GvcApplet +{ + GObject parent; + GvcAppletPrivate *priv; +}; + +struct _GvcAppletClass +{ + GObjectClass parent_class; +}; + +GType gvc_applet_get_type (void) G_GNUC_CONST; + +GvcApplet * gvc_applet_new (void); +void gvc_applet_start (GvcApplet *applet); + +G_END_DECLS + +#endif /* __GVC_APPLET_H */ diff --git a/mate-volume-control/gvc-balance-bar.c b/mate-volume-control/gvc-balance-bar.c new file mode 100644 index 0000000..7431773 --- /dev/null +++ b/mate-volume-control/gvc-balance-bar.c @@ -0,0 +1,552 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "gvc-balance-bar.h" + +#define BALANCE_BAR_STYLE \ + "style \"balance-bar-scale-style\" {\n" \ + " GtkScale::trough-side-details = 0\n" \ + "}\n" \ + "widget \"*.balance-bar-scale\" style : rc \"balance-bar-scale-style\"\n" + +#define SCALE_SIZE 128 +#define GVC_BALANCE_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBarPrivate)) + +struct _GvcBalanceBarPrivate +{ + GvcBalanceType btype; + GtkWidget *scale_box; + GtkWidget *start_box; + GtkWidget *end_box; + GtkWidget *label; + GtkWidget *scale; + GtkAdjustment *adjustment; + GtkSizeGroup *size_group; + gboolean symmetric; + MateMixerStreamControl *control; + gint lfe_channel; +}; + +enum +{ + PROP_0, + PROP_CONTROL, + PROP_BALANCE_TYPE, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void gvc_balance_bar_class_init (GvcBalanceBarClass *klass); +static void gvc_balance_bar_init (GvcBalanceBar *balance_bar); +static void gvc_balance_bar_dispose (GObject *object); + +static gboolean on_scale_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcBalanceBar *bar); + +static void on_adjustment_value_changed (GtkAdjustment *adjustment, + GvcBalanceBar *bar); + +#if GTK_CHECK_VERSION (3, 0, 0) +G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_BOX) +#else +G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_HBOX) +#endif + +static void +create_scale_box (GvcBalanceBar *bar) +{ +#if GTK_CHECK_VERSION (3, 0, 0) + bar->priv->scale_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + bar->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + bar->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + bar->priv->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, + bar->priv->adjustment); +#if GTK_CHECK_VERSION (3, 4, 0) + /* Balance and fade scales do not have an origin */ + if (bar->priv->btype != BALANCE_TYPE_LFE) + gtk_scale_set_has_origin (GTK_SCALE (bar->priv->scale), FALSE); +#endif +#else + bar->priv->scale_box = gtk_hbox_new (FALSE, 6); + bar->priv->start_box = gtk_hbox_new (FALSE, 6); + bar->priv->end_box = gtk_hbox_new (FALSE, 6); + bar->priv->scale = gtk_hscale_new (bar->priv->adjustment); + + /* GTK2 way to remove the origin */ + if (bar->priv->btype != BALANCE_TYPE_LFE) { + gtk_rc_parse_string (BALANCE_BAR_STYLE); + gtk_widget_set_name (bar->priv->scale, "balance-bar-scale"); + } +#endif + + gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->start_box, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->label, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->scale, + TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->end_box, + FALSE, FALSE, 0); + + ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE); + + gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK); + + g_signal_connect (G_OBJECT (bar->priv->scale), + "scroll-event", + G_CALLBACK (on_scale_scroll_event), + bar); + + if (bar->priv->size_group != NULL) { + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->start_box); + + if (bar->priv->symmetric) + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->end_box); + } + + gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE); +} + +static void +update_scale_marks (GvcBalanceBar *bar) +{ + gchar *str_lower = NULL, + *str_upper = NULL; + gdouble lower, + upper; + + gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale)); + + switch (bar->priv->btype) { + case BALANCE_TYPE_RL: + str_lower = g_strdup_printf ("%s", C_("balance", "Left")); + str_upper = g_strdup_printf ("%s", C_("balance", "Right")); + break; + case BALANCE_TYPE_FR: + str_lower = g_strdup_printf ("%s", C_("balance", "Rear")); + str_upper = g_strdup_printf ("%s", C_("balance", "Front")); + break; + case BALANCE_TYPE_LFE: + str_lower = g_strdup_printf ("%s", C_("balance", "Minimum")); + str_upper = g_strdup_printf ("%s", C_("balance", "Maximum")); + break; + } + + lower = gtk_adjustment_get_lower (bar->priv->adjustment); + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + lower, + GTK_POS_BOTTOM, + str_lower); + upper = gtk_adjustment_get_upper (bar->priv->adjustment); + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + upper, + GTK_POS_BOTTOM, + str_upper); + g_free (str_lower); + g_free (str_upper); + + if (bar->priv->btype != BALANCE_TYPE_LFE) + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + (upper - lower) / 2 + lower, + GTK_POS_BOTTOM, + NULL); +} + +void +gvc_balance_bar_set_size_group (GvcBalanceBar *bar, + GtkSizeGroup *group, + gboolean symmetric) +{ + g_return_if_fail (GVC_IS_BALANCE_BAR (bar)); + g_return_if_fail (GTK_IS_SIZE_GROUP (group)); + + bar->priv->size_group = group; + bar->priv->symmetric = symmetric; + + if (bar->priv->size_group != NULL) { + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->start_box); + + if (bar->priv->symmetric) + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->end_box); + } + gtk_widget_queue_draw (GTK_WIDGET (bar)); +} + +static void +update_balance_value (GvcBalanceBar *bar) +{ + gdouble value = 0; + + switch (bar->priv->btype) { + case BALANCE_TYPE_RL: + value = mate_mixer_stream_control_get_balance (bar->priv->control); + g_debug ("Balance value changed to %.2f", value); + break; + case BALANCE_TYPE_FR: + value = mate_mixer_stream_control_get_fade (bar->priv->control); + g_debug ("Fade value changed to %.2f", value); + break; + case BALANCE_TYPE_LFE: + value = mate_mixer_stream_control_get_channel_volume (bar->priv->control, + bar->priv->lfe_channel); + + g_debug ("Subwoofer volume changed to %.0f", value); + break; + } + + gtk_adjustment_set_value (bar->priv->adjustment, value); +} + +static void +on_balance_value_changed (MateMixerStream *stream, + GParamSpec *pspec, + GvcBalanceBar *bar) +{ + update_balance_value (bar); +} + +static gint +find_stream_lfe_channel (MateMixerStreamControl *control) +{ + guint i; + + for (i = 0; i < mate_mixer_stream_control_get_num_channels (control); i++) { + MateMixerChannelPosition position; + + position = mate_mixer_stream_control_get_channel_position (control, i); + if (position == MATE_MIXER_CHANNEL_LFE) + return i; + } + + return -1; +} + +static void +gvc_balance_bar_set_control (GvcBalanceBar *bar, MateMixerStreamControl *control) +{ + g_return_if_fail (GVC_BALANCE_BAR (bar)); + g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control)); + + if (bar->priv->control != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), + on_balance_value_changed, + bar); + g_object_unref (bar->priv->control); + } + + bar->priv->control = g_object_ref (control); + + if (bar->priv->btype == BALANCE_TYPE_LFE) { + gdouble minimum; + gdouble maximum; + + minimum = mate_mixer_stream_control_get_min_volume (bar->priv->control); + maximum = mate_mixer_stream_control_get_normal_volume (bar->priv->control); + + /* Configure the adjustment for the volume limits of the current + * stream. + * Only subwoofer scale uses volume, balance and fade use fixed + * limits which do not need to be updated as balance type is + * only set during construction. */ + gtk_adjustment_configure (GTK_ADJUSTMENT (bar->priv->adjustment), + gtk_adjustment_get_value (bar->priv->adjustment), + minimum, + maximum, + (maximum - minimum) / 100.0, + (maximum - minimum) / 10.0, + 0.0); + + bar->priv->lfe_channel = find_stream_lfe_channel (bar->priv->control); + + if (G_LIKELY (bar->priv->lfe_channel > -1)) + g_debug ("Found LFE channel at position %d", bar->priv->lfe_channel); + else + g_warn_if_reached (); + } else + bar->priv->lfe_channel = -1; + + switch (bar->priv->btype) { + case BALANCE_TYPE_RL: + g_signal_connect (G_OBJECT (bar->priv->control), + "notify::balance", + G_CALLBACK (on_balance_value_changed), + bar); + break; + case BALANCE_TYPE_FR: + g_signal_connect (G_OBJECT (bar->priv->control), + "notify::fade", + G_CALLBACK (on_balance_value_changed), + bar); + break; + case BALANCE_TYPE_LFE: + g_signal_connect (G_OBJECT (bar->priv->control), + "notify::volume", + G_CALLBACK (on_balance_value_changed), + bar); + break; + } + + update_balance_value (bar); + update_scale_marks (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_CONTROL]); +} + +static void +gvc_balance_bar_set_balance_type (GvcBalanceBar *bar, GvcBalanceType btype) +{ + GtkWidget *frame; + GtkAdjustment *adjustment; + + /* Create adjustment with limits for balance and fade types because + * some limits must be provided. + * If subwoofer type is used instead, the limits will be changed when + * stream is set. */ + adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -1.0, 1.0, 0.05, 0.5, 0.0)); + + bar->priv->btype = btype; + bar->priv->adjustment = GTK_ADJUSTMENT (g_object_ref_sink (adjustment)); + + g_signal_connect (G_OBJECT (adjustment), + "value-changed", + G_CALLBACK (on_adjustment_value_changed), + bar); + + switch (btype) { + case BALANCE_TYPE_RL: + bar->priv->label = gtk_label_new_with_mnemonic (_("_Balance:")); + break; + case BALANCE_TYPE_FR: + bar->priv->label = gtk_label_new_with_mnemonic (_("_Fade:")); + break; + case BALANCE_TYPE_LFE: + bar->priv->label = gtk_label_new_with_mnemonic (_("_Subwoofer:")); + break; + } + + gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.0); + + /* Frame */ + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0); + + /* Box with scale */ + create_scale_box (bar); + gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); + + gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label), + bar->priv->scale); + + gtk_widget_set_direction (bar->priv->scale, GTK_TEXT_DIR_LTR); + gtk_widget_show_all (frame); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_BALANCE_TYPE]); +} + +static void +gvc_balance_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GvcBalanceBar *self = GVC_BALANCE_BAR (object); + + switch (prop_id) { + case PROP_CONTROL: + gvc_balance_bar_set_control (self, g_value_get_object (value)); + break; + case PROP_BALANCE_TYPE: + gvc_balance_bar_set_balance_type (self, g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_balance_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GvcBalanceBar *self = GVC_BALANCE_BAR (object); + + switch (prop_id) { + case PROP_CONTROL: + g_value_set_object (value, self->priv->control); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_balance_bar_class_init (GvcBalanceBarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gvc_balance_bar_dispose; + object_class->set_property = gvc_balance_bar_set_property; + object_class->get_property = gvc_balance_bar_get_property; + + properties[PROP_CONTROL] = + g_param_spec_object ("control", + "Control", + "MateMixer stream control", + MATE_MIXER_TYPE_STREAM_CONTROL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_BALANCE_TYPE] = + g_param_spec_int ("balance-type", + "balance type", + "Whether the balance is right-left or front-rear", + BALANCE_TYPE_RL, + NUM_BALANCE_TYPES - 1, + BALANCE_TYPE_RL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (klass, sizeof (GvcBalanceBarPrivate)); +} + +static gboolean +on_scale_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcBalanceBar *bar) +{ + gdouble value; + gdouble minimum; + gdouble maximum; + gdouble step; + + value = gtk_adjustment_get_value (bar->priv->adjustment); + minimum = gtk_adjustment_get_lower (bar->priv->adjustment); + maximum = gtk_adjustment_get_upper (bar->priv->adjustment); + + // XXX fix this for GTK3 + + if (bar->priv->btype == BALANCE_TYPE_LFE) + step = (maximum - minimum) / 100.0; + else + step = 0.05; + + if (event->direction == GDK_SCROLL_UP) { + if (value + step > maximum) + value = maximum; + else + value = value + step; + } else if (event->direction == GDK_SCROLL_DOWN) { + if (value - step < minimum) + value = minimum; + else + value = value - step; + } + + gtk_adjustment_set_value (bar->priv->adjustment, value); + return TRUE; +} + +static void +on_adjustment_value_changed (GtkAdjustment *adjustment, GvcBalanceBar *bar) +{ + gdouble value; + + if (bar->priv->control == NULL) + return; + + value = gtk_adjustment_get_value (adjustment); + + switch (bar->priv->btype) { + case BALANCE_TYPE_RL: + mate_mixer_stream_control_set_balance (bar->priv->control, value); + break; + case BALANCE_TYPE_FR: + mate_mixer_stream_control_set_fade (bar->priv->control, value); + break; + case BALANCE_TYPE_LFE: + mate_mixer_stream_control_set_channel_volume (bar->priv->control, + bar->priv->lfe_channel, + value); + break; + } +} + +static void +gvc_balance_bar_init (GvcBalanceBar *bar) +{ + bar->priv = GVC_BALANCE_BAR_GET_PRIVATE (bar); +} + +static void +gvc_balance_bar_dispose (GObject *object) +{ + GvcBalanceBar *bar; + + bar = GVC_BALANCE_BAR (object); + + if (bar->priv->control != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), + on_balance_value_changed, + bar); + g_clear_object (&bar->priv->control); + } + + G_OBJECT_CLASS (gvc_balance_bar_parent_class)->dispose (object); +} + +GtkWidget * +gvc_balance_bar_new (MateMixerStreamControl *control, GvcBalanceType btype) +{ + return g_object_new (GVC_TYPE_BALANCE_BAR, + "balance-type", btype, + "control", control, +#if GTK_CHECK_VERSION (3, 0, 0) + "orientation", GTK_ORIENTATION_HORIZONTAL, +#endif + NULL); +} diff --git a/mate-volume-control/gvc-balance-bar.h b/mate-volume-control/gvc-balance-bar.h new file mode 100644 index 0000000..d5b142d --- /dev/null +++ b/mate-volume-control/gvc-balance-bar.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_BALANCE_BAR_H +#define __GVC_BALANCE_BAR_H + +#include +#include +#include + +#include + +G_BEGIN_DECLS + +#define GVC_TYPE_BALANCE_BAR (gvc_balance_bar_get_type ()) +#define GVC_BALANCE_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBar)) +#define GVC_BALANCE_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_BALANCE_BAR, GvcBalanceBarClass)) +#define GVC_IS_BALANCE_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_BALANCE_BAR)) +#define GVC_IS_BALANCE_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_BALANCE_BAR)) +#define GVC_BALANCE_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBarClass)) + +typedef enum { + BALANCE_TYPE_RL, + BALANCE_TYPE_FR, + BALANCE_TYPE_LFE, +} GvcBalanceType; + +#define NUM_BALANCE_TYPES BALANCE_TYPE_LFE + 1 + +typedef struct _GvcBalanceBar GvcBalanceBar; +typedef struct _GvcBalanceBarClass GvcBalanceBarClass; +typedef struct _GvcBalanceBarPrivate GvcBalanceBarPrivate; + +struct _GvcBalanceBar +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBox parent; +#else + GtkHBox parent; +#endif + GvcBalanceBarPrivate *priv; +}; + +struct _GvcBalanceBarClass +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBoxClass parent_class; +#else + GtkHBoxClass parent_class; +#endif +}; + +GType gvc_balance_bar_get_type (void) G_GNUC_CONST; + +GtkWidget * gvc_balance_bar_new (MateMixerStreamControl *control, + GvcBalanceType btype); + +void gvc_balance_bar_set_size_group (GvcBalanceBar *bar, + GtkSizeGroup *group, + gboolean symmetric); + +G_END_DECLS + +#endif /* __GVC_BALANCE_BAR_H */ diff --git a/mate-volume-control/gvc-channel-bar.c b/mate-volume-control/gvc-channel-bar.c new file mode 100644 index 0000000..e431651 --- /dev/null +++ b/mate-volume-control/gvc-channel-bar.c @@ -0,0 +1,1158 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include "gvc-channel-bar.h" + +#define SCALE_SIZE 128 +#define GVC_CHANNEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarPrivate)) + +struct _GvcChannelBarPrivate +{ + GtkOrientation orientation; + GtkWidget *scale_box; + GtkWidget *start_box; + GtkWidget *end_box; + GtkWidget *image; + GtkWidget *label; + GtkWidget *low_image; + GtkWidget *scale; + GtkWidget *high_image; + GtkWidget *mute_box; + GtkWidget *mute_button; + GtkAdjustment *adjustment; + gboolean show_icons; + gboolean show_mute; + gboolean show_marks; + gboolean extended; + GtkSizeGroup *size_group; + gboolean symmetric; + gboolean click_lock; + MateMixerStreamControl *control; + MateMixerStreamControlFlags control_flags; +}; + +enum { + PROP_0, + PROP_CONTROL, + PROP_ORIENTATION, + PROP_SHOW_ICONS, + PROP_SHOW_MUTE, + PROP_SHOW_MARKS, + PROP_EXTENDED, + PROP_NAME, + PROP_ICON_NAME, + PROP_LOW_ICON_NAME, + PROP_HIGH_ICON_NAME, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void gvc_channel_bar_class_init (GvcChannelBarClass *klass); +static void gvc_channel_bar_init (GvcChannelBar *bar); + +static gboolean on_scale_button_press_event (GtkWidget *widget, + GdkEventButton *event, + GvcChannelBar *bar); +static gboolean on_scale_button_release_event (GtkWidget *widget, + GdkEventButton *event, + GvcChannelBar *bar); +static gboolean on_scale_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcChannelBar *bar); + +#if GTK_CHECK_VERSION (3, 0, 0) +G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_BOX) +#else +G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_HBOX) +#endif + +static void +create_scale_box (GvcChannelBar *bar) +{ +#if GTK_CHECK_VERSION (3, 0, 0) + bar->priv->scale_box = gtk_box_new (bar->priv->orientation, 6); + bar->priv->start_box = gtk_box_new (bar->priv->orientation, 6); + bar->priv->end_box = gtk_box_new (bar->priv->orientation, 6); + bar->priv->scale = gtk_scale_new (bar->priv->orientation, + bar->priv->adjustment); +#else + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + bar->priv->scale_box = gtk_vbox_new (FALSE, 6); + bar->priv->start_box = gtk_vbox_new (FALSE, 6); + bar->priv->end_box = gtk_vbox_new (FALSE, 6); + bar->priv->scale = gtk_vscale_new (bar->priv->adjustment); + } else { + bar->priv->scale_box = gtk_hbox_new (FALSE, 6); + bar->priv->start_box = gtk_hbox_new (FALSE, 6); + bar->priv->end_box = gtk_hbox_new (FALSE, 6); + bar->priv->scale = gtk_hscale_new (bar->priv->adjustment); + } +#endif + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + gtk_widget_set_size_request (bar->priv->scale, -1, SCALE_SIZE); + + gtk_range_set_inverted (GTK_RANGE (bar->priv->scale), TRUE); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->start_box, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->label, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->high_image, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->scale, + TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->end_box, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->low_image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->mute_box, + FALSE, FALSE, 0); + } else { + gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->start_box, + FALSE, FALSE, 0); + + gtk_box_pack_end (GTK_BOX (bar->priv->start_box), + bar->priv->low_image, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->label, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->scale, + TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->end_box, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->high_image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->mute_box, + FALSE, FALSE, 0); + } + + if (bar->priv->show_icons) { + gtk_widget_show (bar->priv->low_image); + gtk_widget_show (bar->priv->high_image); + } else { + gtk_widget_hide (bar->priv->low_image); + gtk_widget_hide (bar->priv->high_image); + } + + ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE); + + gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK); + + g_signal_connect (G_OBJECT (bar->priv->scale), + "button-press-event", + G_CALLBACK (on_scale_button_press_event), + bar); + g_signal_connect (G_OBJECT (bar->priv->scale), + "button-release-event", + G_CALLBACK (on_scale_button_release_event), + bar); + g_signal_connect (G_OBJECT (bar->priv->scale), + "scroll-event", + G_CALLBACK (on_scale_scroll_event), + bar); + + if (bar->priv->size_group != NULL) { + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->start_box); + + if (bar->priv->symmetric) + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->end_box); + } + + gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE); +} + +static void +on_adjustment_value_changed (GtkAdjustment *adjustment, + GvcChannelBar *bar) +{ + gdouble value; + gdouble lower; + + if (bar->priv->control == NULL || bar->priv->click_lock == TRUE) + return; + + value = gtk_adjustment_get_value (bar->priv->adjustment); + lower = gtk_adjustment_get_lower (bar->priv->adjustment); + + if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE) + mate_mixer_stream_control_set_mute (bar->priv->control, (value <= lower)); + + if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE) + mate_mixer_stream_control_set_volume (bar->priv->control, (guint) value); +} + +static void +on_mute_button_toggled (GtkToggleButton *button, GvcChannelBar *bar) +{ + gboolean mute; + + mute = gtk_toggle_button_get_active (button); + + mate_mixer_stream_control_set_mute (bar->priv->control, mute); +} + +static void +update_layout (GvcChannelBar *bar) +{ + GtkWidget *frame; + + if (bar->priv->scale == NULL) + return; + + frame = gtk_widget_get_parent (bar->priv->scale_box); + + g_object_ref (bar->priv->image); + g_object_ref (bar->priv->label); + g_object_ref (bar->priv->mute_box); + g_object_ref (bar->priv->low_image); + g_object_ref (bar->priv->high_image); + + // XXX this is not the opposite of what is done above + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->image); + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->label); + gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), + bar->priv->mute_box); + + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->low_image); + gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), + bar->priv->high_image); + } else { + gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), + bar->priv->low_image); + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->high_image); + } + + gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), + bar->priv->start_box); + gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), + bar->priv->scale); + gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), + bar->priv->end_box); + gtk_container_remove (GTK_CONTAINER (frame), + bar->priv->scale_box); + + create_scale_box (bar); + gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); + + g_object_unref (bar->priv->image); + g_object_unref (bar->priv->label); + g_object_unref (bar->priv->mute_box); + g_object_unref (bar->priv->low_image); + g_object_unref (bar->priv->high_image); + + gtk_widget_show_all (frame); +} + +static void +update_marks (GvcChannelBar *bar) +{ + gdouble base; + gdouble normal; + gboolean has_mark = FALSE; + + gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale)); + + if (bar->priv->control == NULL || bar->priv->show_marks == FALSE) + return; + + /* Base volume represents unamplified volume, normal volume is the 100% + * volume, in many cases they are the same as unamplified volume is unknown */ + base = mate_mixer_stream_control_get_base_volume (bar->priv->control); + normal = mate_mixer_stream_control_get_normal_volume (bar->priv->control); + + if (normal <= gtk_adjustment_get_lower (bar->priv->adjustment)) + return; + + if (base < normal) { + gchar *str = g_strdup_printf ("%s", C_("volume", "Unamplified")); + + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + base, + GTK_POS_BOTTOM, + str); + has_mark = TRUE; + g_free (str); + } + + /* Only show 100% mark if the scale is extended beyond 100% and + * there is no unamplified mark or it is below the normal volume */ + if (bar->priv->extended && (base == normal || base < normal)) { + gchar *str = g_strdup_printf ("%s", C_("volume", "100%")); + + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + normal, + GTK_POS_BOTTOM, + str); + has_mark = TRUE; + g_free (str); + } + + if (has_mark) { + gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0, 0, 0); + + gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0); + gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0); + gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0); + } else { + gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0.5, 0, 0); + + gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0.5); + gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0.5); + gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0.5); + } +} + +static void +update_adjustment_value (GvcChannelBar *bar) +{ + gdouble value; + gboolean set_lower = FALSE; + + /* Move the slider to the minimal value if the stream control is muted or + * volume is unavailable */ + if (bar->priv->control == NULL) + set_lower = TRUE; + else if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) + set_lower = mate_mixer_stream_control_get_mute (bar->priv->control); + + if (set_lower == TRUE) + value = gtk_adjustment_get_lower (bar->priv->adjustment); + else + value = mate_mixer_stream_control_get_volume (bar->priv->control); + + g_signal_handlers_block_by_func (G_OBJECT (bar->priv->adjustment), + on_adjustment_value_changed, + bar); + + gtk_adjustment_set_value (bar->priv->adjustment, value); + + g_signal_handlers_unblock_by_func (G_OBJECT (bar->priv->adjustment), + on_adjustment_value_changed, + bar); +} + +static void +update_adjustment_limits (GvcChannelBar *bar) +{ + gdouble minimum = 0.0; + gdouble maximum = 0.0; + + if (bar->priv->control != NULL) { + minimum = mate_mixer_stream_control_get_min_volume (bar->priv->control); + if (bar->priv->extended) + maximum = mate_mixer_stream_control_get_max_volume (bar->priv->control); + else + maximum = mate_mixer_stream_control_get_normal_volume (bar->priv->control); + } + + gtk_adjustment_configure (bar->priv->adjustment, + gtk_adjustment_get_value (bar->priv->adjustment), + minimum, + maximum, + (maximum - minimum) / 100.0, + (maximum - minimum) / 15.0, + 0.0); +} + +static void +update_mute_button (GvcChannelBar *bar) +{ + if (bar->priv->show_mute == TRUE) { + gboolean enable = FALSE; + + if (bar->priv->control != NULL && + bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) + enable = TRUE; + + if (enable == TRUE) { + gboolean mute = mate_mixer_stream_control_get_mute (bar->priv->control); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), + mute); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), + FALSE); + } + + gtk_widget_set_sensitive (bar->priv->mute_button, enable); + gtk_widget_show (bar->priv->mute_button); + } else + gtk_widget_hide (bar->priv->mute_button); +} + +static gboolean +on_scale_button_press_event (GtkWidget *widget, + GdkEventButton *event, + GvcChannelBar *bar) +{ + +#if !GTK_CHECK_VERSION (3, 6, 0) + /* Up to GTK 3.4 the slider selection only moves in increments when + * clicking in the slider with the left button and it moves directly + * to the clicked position with the middle mouse button. + * Change this behaviour to also jump to the clicked position with the + * left mouse button. */ + if (event->button == 1) + event->button = 2; +#endif + + /* Muting the stream when volume is non-zero moves the slider to zero, + * but the volume remains the same. In this case delay unmuting and + * changing volume until user releases the mouse button. */ + if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE && + bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { + if (mate_mixer_stream_control_get_mute (bar->priv->control) == TRUE) { + guint minimum = (guint) gtk_adjustment_get_lower (bar->priv->adjustment); + + if (mate_mixer_stream_control_get_volume (bar->priv->control) > minimum) + bar->priv->click_lock = TRUE; + } + } + return FALSE; +} + +static gboolean +on_scale_button_release_event (GtkWidget *widget, + GdkEventButton *event, + GvcChannelBar *bar) +{ +#if !GTK_CHECK_VERSION (3, 6, 0) + if (event->button == 1) + event->button = 2; +#endif + + if (bar->priv->click_lock == TRUE) { + /* The volume change is not reflected while the lock is + * held, propagate the change now that user has released + * the mouse button */ + bar->priv->click_lock = FALSE; + on_adjustment_value_changed (bar->priv->adjustment, bar); + } + + /* Play a sound */ + ca_gtk_play_for_widget (GTK_WIDGET (bar), 0, + CA_PROP_EVENT_ID, "audio-volume-change", + CA_PROP_EVENT_DESCRIPTION, "Volume change", + CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", + CA_PROP_APPLICATION_NAME, _("Volume Control"), + CA_PROP_APPLICATION_VERSION, VERSION, + CA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control", + NULL); + return FALSE; +} + +static gboolean +on_scale_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcChannelBar *bar) +{ + GdkScrollDirection direction = event->direction; + +#if GTK_CHECK_VERSION (3, 4, 0) + if (direction == GDK_SCROLL_SMOOTH) { + gdouble dx = 0.0; + gdouble dy = 0.0; + + gdk_event_get_scroll_deltas ((const GdkEvent *) event, &dx, &dy); + if (dy > 0.0) + direction = GDK_SCROLL_DOWN; + else if (dy < 0.0) + direction = GDK_SCROLL_UP; + else + return FALSE; + } +#endif + return gvc_channel_bar_scroll (bar, direction); +} + +static void +on_control_volume_notify (MateMixerStreamControl *control, + GParamSpec *pspec, + GvcChannelBar *bar) +{ + update_adjustment_value (bar); +} + +static void +on_control_mute_notify (MateMixerStreamControl *control, + GParamSpec *pspec, + GvcChannelBar *bar) +{ + if (bar->priv->show_mute == TRUE) { + gboolean mute = mate_mixer_stream_control_get_mute (control); + + g_signal_handlers_block_by_func (G_OBJECT (bar->priv->mute_button), + on_mute_button_toggled, + bar); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), mute); + + g_signal_handlers_unblock_by_func (G_OBJECT (bar->priv->mute_button), + on_mute_button_toggled, + bar); + } + update_adjustment_value (bar); +} + +MateMixerStreamControl * +gvc_channel_bar_get_control (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + return bar->priv->control; +} + +void +gvc_channel_bar_set_control (GvcChannelBar *bar, MateMixerStreamControl *control) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (bar->priv->control == control) + return; + + if (control != NULL) + g_object_ref (control); + + if (bar->priv->control != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), + G_CALLBACK (on_control_volume_notify), + bar); + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), + G_CALLBACK (on_control_mute_notify), + bar); + g_object_unref (bar->priv->control); + } + + bar->priv->control = control; + + if (control != NULL) + bar->priv->control_flags = mate_mixer_stream_control_get_flags (control); + else + bar->priv->control_flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS; + + if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) + g_signal_connect (G_OBJECT (control), + "notify::volume", + G_CALLBACK (on_control_volume_notify), + bar); + if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) + g_signal_connect (G_OBJECT (control), + "notify::mute", + G_CALLBACK (on_control_mute_notify), + bar); + + update_marks (bar); + update_mute_button (bar); + update_adjustment_limits (bar); + update_adjustment_value (bar); +} + +GtkOrientation +gvc_channel_bar_get_orientation (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), 0); + + return bar->priv->orientation; +} + +void +gvc_channel_bar_set_orientation (GvcChannelBar *bar, + GtkOrientation orientation) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (orientation == bar->priv->orientation) + return; + + bar->priv->orientation = orientation; + update_layout (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]); +} + +gboolean +gvc_channel_bar_get_show_icons (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + return bar->priv->show_icons; +} + +void +gvc_channel_bar_set_show_icons (GvcChannelBar *bar, gboolean show_icons) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (show_icons == bar->priv->show_icons) + return; + + bar->priv->show_icons = show_icons; + + if (bar->priv->show_icons == TRUE) { + gtk_widget_show (bar->priv->low_image); + gtk_widget_show (bar->priv->high_image); + } else { + gtk_widget_hide (bar->priv->low_image); + gtk_widget_hide (bar->priv->high_image); + } + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_ICONS]); +} + +gboolean +gvc_channel_bar_get_show_mute (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + return bar->priv->show_mute; +} + +void +gvc_channel_bar_set_show_mute (GvcChannelBar *bar, gboolean show_mute) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (show_mute == bar->priv->show_mute) + return; + + bar->priv->show_mute = show_mute; + update_mute_button (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MUTE]); +} + +gboolean +gvc_channel_bar_get_show_marks (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + return bar->priv->show_marks; +} + +void +gvc_channel_bar_set_show_marks (GvcChannelBar *bar, gboolean show_marks) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (show_marks == bar->priv->show_marks) + return; + + bar->priv->show_marks = show_marks; + update_marks (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MARKS]); +} + +gboolean +gvc_channel_bar_get_extended (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + return bar->priv->extended; +} + +void +gvc_channel_bar_set_extended (GvcChannelBar *bar, gboolean extended) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (extended == bar->priv->extended) + return; + + bar->priv->extended = extended; + + /* Update displayed marks as non-extended scales do not show the 100% + * limit at the end of the scale */ + update_marks (bar); + update_adjustment_limits (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_EXTENDED]); +} + +const gchar * +gvc_channel_bar_get_name (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + return gtk_label_get_text (GTK_LABEL (bar->priv->label)); +} + +void +gvc_channel_bar_set_name (GvcChannelBar *bar, const gchar *name) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (name != NULL) { + gtk_label_set_text_with_mnemonic (GTK_LABEL (bar->priv->label), name); + gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label), + bar->priv->scale); + + gtk_widget_show (bar->priv->label); + } else { + gtk_label_set_text (GTK_LABEL (bar->priv->label), NULL); + gtk_widget_hide (bar->priv->label); + } + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_NAME]); +} + +const gchar * +gvc_channel_bar_get_icon_name (GvcChannelBar *bar) +{ + const gchar *name = NULL; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + gtk_image_get_icon_name (GTK_IMAGE (bar->priv->image), &name, NULL); + return name; +} + +void +gvc_channel_bar_set_icon_name (GvcChannelBar *bar, const gchar *name) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->image), + name, + GTK_ICON_SIZE_DIALOG); + if (name != NULL) + gtk_widget_show (bar->priv->image); + else + gtk_widget_hide (bar->priv->image); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ICON_NAME]); +} + +const gchar * +gvc_channel_bar_get_low_icon_name (GvcChannelBar *bar) +{ + const gchar *name = NULL; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + gtk_image_get_icon_name (GTK_IMAGE (bar->priv->low_image), &name, NULL); + return name; +} + +void +gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, const gchar *name) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->low_image), + name, + GTK_ICON_SIZE_BUTTON); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_LOW_ICON_NAME]); +} + +const gchar * +gvc_channel_bar_get_high_icon_name (GvcChannelBar *bar) +{ + const gchar *name = NULL; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + gtk_image_get_icon_name (GTK_IMAGE (bar->priv->high_image), &name, NULL); + return name; +} + +void +gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, const gchar *name) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->high_image), + name, + GTK_ICON_SIZE_BUTTON); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_HIGH_ICON_NAME]); +} + +gboolean +gvc_channel_bar_scroll (GvcChannelBar *bar, GdkScrollDirection direction) +{ + gdouble value; + gdouble minimum; + gdouble maximum; + gdouble scrollstep; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + if (direction != GDK_SCROLL_UP && direction != GDK_SCROLL_DOWN) + return FALSE; + } else { + /* Switch direction for RTL */ + if (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_RTL) { + if (direction == GDK_SCROLL_RIGHT) + direction = GDK_SCROLL_LEFT; + else if (direction == GDK_SCROLL_LEFT) + direction = GDK_SCROLL_RIGHT; + } + + /* Switch side scroll to vertical */ + if (direction == GDK_SCROLL_RIGHT) + direction = GDK_SCROLL_UP; + else if (direction == GDK_SCROLL_LEFT) + direction = GDK_SCROLL_DOWN; + } + + value = gtk_adjustment_get_value (bar->priv->adjustment); + minimum = gtk_adjustment_get_lower (bar->priv->adjustment); + maximum = gtk_adjustment_get_upper (bar->priv->adjustment); + + scrollstep = maximum / 100.0 * 5.0; + + if (direction == GDK_SCROLL_UP) { + if (value + scrollstep > maximum) + value = maximum; + else + value = value + scrollstep; + } else if (direction == GDK_SCROLL_DOWN) { + if (value - scrollstep < minimum) + value = minimum; + else + value = value - scrollstep; + } + + gtk_adjustment_set_value (bar->priv->adjustment, value); + return TRUE; +} + +void +gvc_channel_bar_set_size_group (GvcChannelBar *bar, + GtkSizeGroup *group, + gboolean symmetric) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + g_return_if_fail (GTK_IS_SIZE_GROUP (group)); + + bar->priv->size_group = group; + bar->priv->symmetric = symmetric; + + if (bar->priv->size_group != NULL) { + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->start_box); + + if (bar->priv->symmetric) + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->end_box); + } + gtk_widget_queue_draw (GTK_WIDGET (bar)); +} + +static void +gvc_channel_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GvcChannelBar *self = GVC_CHANNEL_BAR (object); + + switch (prop_id) { + case PROP_CONTROL: + gvc_channel_bar_set_control (self, g_value_get_object (value)); + break; + case PROP_ORIENTATION: + gvc_channel_bar_set_orientation (self, g_value_get_enum (value)); + break; + case PROP_SHOW_MUTE: + gvc_channel_bar_set_show_mute (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_ICONS: + gvc_channel_bar_set_show_icons (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_MARKS: + gvc_channel_bar_set_show_marks (self, g_value_get_boolean (value)); + break; + case PROP_EXTENDED: + gvc_channel_bar_set_extended (self, g_value_get_boolean (value)); + break; + case PROP_NAME: + gvc_channel_bar_set_name (self, g_value_get_string (value)); + break; + case PROP_ICON_NAME: + gvc_channel_bar_set_icon_name (self, g_value_get_string (value)); + break; + case PROP_LOW_ICON_NAME: + gvc_channel_bar_set_low_icon_name (self, g_value_get_string (value)); + break; + case PROP_HIGH_ICON_NAME: + gvc_channel_bar_set_high_icon_name (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_channel_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GvcChannelBar *self = GVC_CHANNEL_BAR (object); + + switch (prop_id) { + case PROP_CONTROL: + g_value_set_object (value, self->priv->control); + break; + case PROP_ORIENTATION: + g_value_set_enum (value, self->priv->orientation); + break; + case PROP_SHOW_MUTE: + g_value_set_boolean (value, self->priv->show_mute); + break; + case PROP_SHOW_ICONS: + g_value_set_boolean (value, self->priv->show_icons); + break; + case PROP_SHOW_MARKS: + g_value_set_boolean (value, self->priv->show_marks); + break; + case PROP_EXTENDED: + g_value_set_boolean (value, self->priv->extended); + break; + case PROP_NAME: + g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_channel_bar_class_init (GvcChannelBarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gvc_channel_bar_set_property; + object_class->get_property = gvc_channel_bar_get_property; + + properties[PROP_CONTROL] = + g_param_spec_object ("control", + "Control", + "MateMixer stream control", + MATE_MIXER_TYPE_STREAM_CONTROL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ORIENTATION] = + g_param_spec_enum ("orientation", + "Orientation", + "The orientation of the scale", + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_VERTICAL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_MUTE] = + g_param_spec_boolean ("show-mute", + "show mute", + "Whether stream is muted", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_ICONS] = + g_param_spec_boolean ("show-icons", + "show mute", + "Whether to show low and high icons", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_MARKS] = + g_param_spec_boolean ("show-marks", + "Show marks", + "Whether to show scale marks", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_EXTENDED] = + g_param_spec_boolean ("extended", + "Extended", + "Allow the scale to be extended above normal volume", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name to display for this stream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ICON_NAME] = + g_param_spec_string ("icon-name", + "Icon name", + "Name of icon to display for this stream", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_LOW_ICON_NAME] = + g_param_spec_string ("low-icon-name", + "Low icon name", + "Name of low volume icon to display for this stream", + "audio-volume-low", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_HIGH_ICON_NAME] = + g_param_spec_string ("high-icon-name", + "High icon name", + "Name of high volume icon to display for this stream", + "audio-volume-high", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (klass, sizeof (GvcChannelBarPrivate)); +} + +static void +gvc_channel_bar_init (GvcChannelBar *bar) +{ + GtkWidget *frame; + + bar->priv = GVC_CHANNEL_BAR_GET_PRIVATE (bar); + + /* Mute button */ + bar->priv->mute_button = gtk_check_button_new_with_label (_("Mute")); + gtk_widget_set_no_show_all (bar->priv->mute_button, TRUE); + + g_signal_connect (bar->priv->mute_button, + "toggled", + G_CALLBACK (on_mute_button_toggled), + bar); + + bar->priv->mute_box = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (bar->priv->mute_box), bar->priv->mute_button); + + bar->priv->image = gtk_image_new (); + gtk_widget_set_no_show_all (bar->priv->image, TRUE); + + /* Low/high icons */ + bar->priv->low_image = gtk_image_new (); + gtk_widget_set_no_show_all (bar->priv->low_image, TRUE); + + bar->priv->high_image = gtk_image_new (); + gtk_widget_set_no_show_all (bar->priv->high_image, TRUE); + + bar->priv->label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.5); + gtk_widget_set_no_show_all (bar->priv->label, TRUE); + + /* Frame */ + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0); + + gtk_widget_show_all (frame); + + /* Create a default adjustment */ + bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 0, 0, 0, 0)); + + g_object_ref_sink (bar->priv->adjustment); + + g_signal_connect (bar->priv->adjustment, + "value-changed", + G_CALLBACK (on_adjustment_value_changed), + bar); + + /* Initially create a vertical scale box */ + bar->priv->orientation = GTK_ORIENTATION_VERTICAL; + + create_scale_box (bar); + + gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); +} + +GtkWidget * +gvc_channel_bar_new (MateMixerStreamControl *control) +{ + return g_object_new (GVC_TYPE_CHANNEL_BAR, + "control", control, +#if GTK_CHECK_VERSION (3, 0, 0) + "orientation", GTK_ORIENTATION_HORIZONTAL, +#endif + NULL); +} diff --git a/mate-volume-control/gvc-channel-bar.h b/mate-volume-control/gvc-channel-bar.h new file mode 100644 index 0000000..1c80be9 --- /dev/null +++ b/mate-volume-control/gvc-channel-bar.h @@ -0,0 +1,118 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_CHANNEL_BAR_H +#define __GVC_CHANNEL_BAR_H + +#include +#include +#include + +#include + +G_BEGIN_DECLS + +#define GVC_TYPE_CHANNEL_BAR (gvc_channel_bar_get_type ()) +#define GVC_CHANNEL_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBar)) +#define GVC_CHANNEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_CHANNEL_BAR, GvcChannelBarClass)) +#define GVC_IS_CHANNEL_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_CHANNEL_BAR)) +#define GVC_IS_CHANNEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_BAR)) +#define GVC_CHANNEL_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarClass)) + +typedef struct _GvcChannelBar GvcChannelBar; +typedef struct _GvcChannelBarClass GvcChannelBarClass; +typedef struct _GvcChannelBarPrivate GvcChannelBarPrivate; + +struct _GvcChannelBar +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBox parent; +#else + GtkHBox parent; +#endif + GvcChannelBarPrivate *priv; +}; + +struct _GvcChannelBarClass +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBoxClass parent_class; +#else + GtkHBoxClass parent_class; +#endif + + void (* changed) (GvcChannelBar *bar); +}; + +GType gvc_channel_bar_get_type (void); + +GtkWidget * gvc_channel_bar_new (MateMixerStreamControl *control); + +MateMixerStreamControl *gvc_channel_bar_get_control (GvcChannelBar *bar); +void gvc_channel_bar_set_control (GvcChannelBar *bar, + MateMixerStreamControl *control); + +const gchar * gvc_channel_bar_get_name (GvcChannelBar *bar); +void gvc_channel_bar_set_name (GvcChannelBar *bar, + const gchar *name); + +const gchar * gvc_channel_bar_get_icon_name (GvcChannelBar *bar); +void gvc_channel_bar_set_icon_name (GvcChannelBar *bar, + const gchar *icon_name); + +const gchar * gvc_channel_bar_get_low_icon_name (GvcChannelBar *bar); +void gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, + const gchar *icon_name); + +const gchar * gvc_channel_bar_get_high_icon_name (GvcChannelBar *bar); +void gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, + const gchar *icon_name); + +GtkOrientation gvc_channel_bar_get_orientation (GvcChannelBar *bar); +void gvc_channel_bar_set_orientation (GvcChannelBar *bar, + GtkOrientation orientation); + +gboolean gvc_channel_bar_get_show_icons (GvcChannelBar *bar); +void gvc_channel_bar_set_show_icons (GvcChannelBar *bar, + gboolean show_mute); + +gboolean gvc_channel_bar_get_show_mute (GvcChannelBar *bar); +void gvc_channel_bar_set_show_mute (GvcChannelBar *bar, + gboolean show_mute); + +gboolean gvc_channel_bar_get_show_marks (GvcChannelBar *bar); +void gvc_channel_bar_set_show_marks (GvcChannelBar *bar, + gboolean show_marks); + +gboolean gvc_channel_bar_get_extended (GvcChannelBar *bar); +void gvc_channel_bar_set_extended (GvcChannelBar *bar, + gboolean extended); + +void gvc_channel_bar_set_size_group (GvcChannelBar *bar, + GtkSizeGroup *group, + gboolean symmetric); + +gboolean gvc_channel_bar_scroll (GvcChannelBar *bar, + GdkScrollDirection direction); + +G_END_DECLS + +#endif /* __GVC_CHANNEL_BAR_H */ diff --git a/mate-volume-control/gvc-combo-box.c b/mate-volume-control/gvc-combo-box.c new file mode 100644 index 0000000..b2a273f --- /dev/null +++ b/mate-volume-control/gvc-combo-box.c @@ -0,0 +1,397 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Bastien Nocera + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include + +#include + +#include "gvc-combo-box.h" + +#define GVC_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_COMBO_BOX, GvcComboBoxPrivate)) + +struct _GvcComboBoxPrivate +{ + GtkWidget *drop_box; + GtkWidget *start_box; + GtkWidget *end_box; + GtkWidget *label; + GtkWidget *button; + GtkTreeModel *model; + GtkWidget *combobox; + gboolean set_called; + GtkSizeGroup *size_group; + gboolean symmetric; +}; + +enum { + COL_NAME, + COL_HUMAN_NAME, + NUM_COLS +}; + +enum { + CHANGED, + BUTTON_CLICKED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +enum { + PROP_0, + PROP_LABEL, + PROP_SHOW_BUTTON, + PROP_BUTTON_LABEL, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void gvc_combo_box_class_init (GvcComboBoxClass *klass); +static void gvc_combo_box_init (GvcComboBox *combo); +static void gvc_combo_box_dispose (GObject *object); + +#if GTK_CHECK_VERSION (3, 0, 0) +G_DEFINE_TYPE (GvcComboBox, gvc_combo_box, GTK_TYPE_BOX) +#else +G_DEFINE_TYPE (GvcComboBox, gvc_combo_box, GTK_TYPE_HBOX) +#endif + +void +gvc_combo_box_set_size_group (GvcComboBox *combobox, + GtkSizeGroup *group, + gboolean symmetric) +{ + g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); + g_return_if_fail (GTK_IS_SIZE_GROUP (group)); + + combobox->priv->size_group = group; + combobox->priv->symmetric = symmetric; + + if (combobox->priv->size_group != NULL) { + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->start_box); + + if (combobox->priv->symmetric) + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->end_box); + } + gtk_widget_queue_draw (GTK_WIDGET (combobox)); +} + +static void +gvc_combo_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GvcComboBox *self = GVC_COMBO_BOX (object); + + switch (prop_id) { + case PROP_LABEL: + gtk_label_set_text_with_mnemonic (GTK_LABEL (self->priv->label), g_value_get_string (value)); + break; + case PROP_BUTTON_LABEL: + gtk_button_set_label (GTK_BUTTON (self->priv->button), g_value_get_string (value)); + break; + case PROP_SHOW_BUTTON: + gtk_widget_set_visible (self->priv->button, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_combo_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GvcComboBox *self = GVC_COMBO_BOX (object); + + switch (prop_id) { + case PROP_LABEL: + g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label))); + break; + case PROP_BUTTON_LABEL: + g_value_set_string (value, gtk_button_get_label (GTK_BUTTON (self->priv->button))); + break; + case PROP_SHOW_BUTTON: + g_value_set_boolean (value, gtk_widget_get_visible (self->priv->button)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_combo_box_class_init (GvcComboBoxClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gvc_combo_box_dispose; + object_class->set_property = gvc_combo_box_set_property; + object_class->get_property = gvc_combo_box_get_property; + + properties[PROP_LABEL] = + g_param_spec_string ("label", + "label", + "The combo box label", + _("_Profile:"), + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + properties[PROP_SHOW_BUTTON] = + g_param_spec_boolean ("show-button", + "show-button", + "Whether to show the button", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + properties[PROP_BUTTON_LABEL] = + g_param_spec_string ("button-label", + "button-label", + "The button's label", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GvcComboBoxClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[BUTTON_CLICKED] = + g_signal_new ("button-clicked", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GvcComboBoxClass, button_clicked), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0, G_TYPE_NONE); + + g_type_class_add_private (klass, sizeof (GvcComboBoxPrivate)); +} + +void +gvc_combo_box_set_options (GvcComboBox *combobox, const GList *options) +{ + const GList *l; + + g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); + g_return_if_fail (combobox->priv->set_called == FALSE); + + for (l = options; l != NULL; l = l->next) { + MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (l->data); + + gtk_list_store_insert_with_values (GTK_LIST_STORE (combobox->priv->model), + NULL, + G_MAXINT, + COL_NAME, + mate_mixer_switch_option_get_name (option), + COL_HUMAN_NAME, + mate_mixer_switch_option_get_label (option), + -1); + } + combobox->priv->set_called = TRUE; +} + +void +gvc_combo_box_set_active (GvcComboBox *combobox, const gchar *id) +{ + GtkTreeIter iter; + gboolean cont; + + g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); + g_return_if_fail (id != NULL); + + cont = gtk_tree_model_get_iter_first (combobox->priv->model, &iter); + while (cont != FALSE) { + gchar *name; + + gtk_tree_model_get (combobox->priv->model, &iter, + COL_NAME, &name, + -1); + if (g_strcmp0 (name, id) == 0) { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox->priv->combobox), &iter); + g_free (name); + return; + } + g_free (name); + + gtk_tree_model_iter_next (combobox->priv->model, &iter); + } + g_warning ("Could not find id '%s' in combo box", id); +} + +static void +on_combo_box_changed (GtkComboBox *widget, GvcComboBox *combobox) +{ + GtkTreeIter iter; + gchar *profile; + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter) == FALSE) { + g_warning ("Could not find an active profile or port"); + return; + } + + gtk_tree_model_get (combobox->priv->model, &iter, + COL_NAME, &profile, + -1); + + g_signal_emit (combobox, signals[CHANGED], 0, profile); + g_free (profile); +} + +static void +on_combo_box_button_clicked (GtkButton *button, GvcComboBox *combobox) +{ + g_signal_emit (combobox, signals[BUTTON_CLICKED], 0); +} + +static void +gvc_combo_box_init (GvcComboBox *combobox) +{ + GtkWidget *frame; + GtkCellRenderer *renderer; + + combobox->priv = GVC_COMBO_BOX_GET_PRIVATE (combobox); + + combobox->priv->model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS, + G_TYPE_STRING, + G_TYPE_STRING)); + combobox->priv->label = gtk_label_new (NULL); + + gtk_misc_set_alignment (GTK_MISC (combobox->priv->label), 0.0, 0.5); + + /* Frame */ + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (combobox), frame, TRUE, TRUE, 0); + +#if GTK_CHECK_VERSION (3, 0, 0) + combobox->priv->drop_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + combobox->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + combobox->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); +#else + combobox->priv->drop_box = gtk_hbox_new (FALSE, 6); + combobox->priv->start_box = gtk_hbox_new (FALSE, 6); + combobox->priv->end_box = gtk_hbox_new (FALSE, 6); +#endif + combobox->priv->combobox = gtk_combo_box_new_with_model (combobox->priv->model); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox->priv->combobox), + renderer, + FALSE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox->priv->combobox), + renderer, + "text", + COL_HUMAN_NAME); + +#if GTK_CHECK_VERSION (3, 0, 0) + /* Make sure the combo box does not get too long on long profile names */ + g_object_set (G_OBJECT (renderer), + "ellipsize", + PANGO_ELLIPSIZE_END, + NULL); + + gtk_combo_box_set_popup_fixed_width (GTK_COMBO_BOX (combobox->priv->combobox), FALSE); +#endif + + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->start_box, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (combobox->priv->start_box), + combobox->priv->label, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->combobox, + TRUE, TRUE, 0); + + combobox->priv->button = gtk_button_new_with_label (""); + + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->button, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->end_box, + FALSE, FALSE, 0); + + gtk_widget_set_no_show_all (combobox->priv->button, TRUE); + + if (combobox->priv->size_group != NULL) { + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->start_box); + + if (combobox->priv->symmetric) + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->end_box); + } + + gtk_label_set_mnemonic_widget (GTK_LABEL (combobox->priv->label), + combobox->priv->combobox); + + gtk_container_add (GTK_CONTAINER (frame), combobox->priv->drop_box); + gtk_widget_show_all (frame); + + g_signal_connect (G_OBJECT (combobox->priv->combobox), + "changed", + G_CALLBACK (on_combo_box_changed), + combobox); + g_signal_connect (G_OBJECT (combobox->priv->button), + "clicked", + G_CALLBACK (on_combo_box_button_clicked), + combobox); +} + +static void +gvc_combo_box_dispose (GObject *object) +{ + GvcComboBox *combobox; + + combobox = GVC_COMBO_BOX (object); + + g_clear_object (&combobox->priv->model); + + G_OBJECT_CLASS (gvc_combo_box_parent_class)->dispose (object); +} + +GtkWidget * +gvc_combo_box_new (const gchar *label) +{ + return g_object_new (GVC_TYPE_COMBO_BOX, + "label", label, +#if GTK_CHECK_VERSION (3, 0, 0) + "orientation", GTK_ORIENTATION_HORIZONTAL, +#endif + NULL); +} diff --git a/mate-volume-control/gvc-combo-box.h b/mate-volume-control/gvc-combo-box.h new file mode 100644 index 0000000..9a2f425 --- /dev/null +++ b/mate-volume-control/gvc-combo-box.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_COMBO_BOX_H +#define __GVC_COMBO_BOX_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define GVC_TYPE_COMBO_BOX (gvc_combo_box_get_type ()) +#define GVC_COMBO_BOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_COMBO_BOX, GvcComboBox)) +#define GVC_COMBO_BOX_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_COMBO_BOX, GvcComboBoxClass)) +#define GVC_IS_COMBO_BOX(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_COMBO_BOX)) +#define GVC_IS_COMBO_BOX_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_COMBO_BOX)) +#define GVC_COMBO_BOX_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_COMBO_BOX, GvcComboBoxClass)) + +typedef struct _GvcComboBox GvcComboBox; +typedef struct _GvcComboBoxClass GvcComboBoxClass; +typedef struct _GvcComboBoxPrivate GvcComboBoxPrivate; + +struct _GvcComboBox +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBox parent; +#else + GtkHBox parent; +#endif + GvcComboBoxPrivate *priv; +}; + +struct _GvcComboBoxClass +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBoxClass parent_class; +#else + GtkHBoxClass parent_class; +#endif + void (* changed) (GvcComboBox *combobox, + const gchar *name); + void (* button_clicked) (GvcComboBox *combobox); +}; + +GType gvc_combo_box_get_type (void) G_GNUC_CONST; + +GtkWidget * gvc_combo_box_new (const gchar *label); + +void gvc_combo_box_set_size_group (GvcComboBox *combobox, + GtkSizeGroup *group, + gboolean symmetric); + +void gvc_combo_box_set_options (GvcComboBox *combobox, + const GList *options); +void gvc_combo_box_set_active (GvcComboBox *combobox, + const gchar *id); + +G_END_DECLS + +#endif /* __GVC_COMBO_BOX_H */ diff --git a/mate-volume-control/gvc-level-bar.c b/mate-volume-control/gvc-level-bar.c new file mode 100644 index 0000000..029b346 --- /dev/null +++ b/mate-volume-control/gvc-level-bar.c @@ -0,0 +1,876 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// XXX on gtk3 the last two squares don't get filled + +#include +#include +#include +#include +#include + +#include "gvc-level-bar.h" +#include "gvc-utils.h" + +#define GVC_LEVEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBarPrivate)) + +#define NUM_BOXES 15 +#define MIN_HORIZONTAL_BAR_WIDTH 150 +#define HORIZONTAL_BAR_HEIGHT 6 +#define VERTICAL_BAR_WIDTH 6 +#define MIN_VERTICAL_BAR_HEIGHT 400 + +typedef struct { + int peak_num; + int max_peak_num; + GdkRectangle area; + int delta; + int box_width; + int box_height; + int box_radius; +#if GTK_CHECK_VERSION (3, 0, 0) + GdkRGBA color_bg; + GdkRGBA color_fg; + GdkRGBA color_dark; +#else + GdkColor color_bg; + GdkColor color_fg; + GdkColor color_dark; +#endif +} LevelBarLayout; + +struct _GvcLevelBarPrivate +{ + GtkOrientation orientation; + GtkAdjustment *peak_adjustment; + GtkAdjustment *rms_adjustment; + GvcLevelScale scale; + gdouble peak_fraction; + gdouble rms_fraction; + gdouble max_peak; + guint max_peak_id; + LevelBarLayout layout; +}; + +enum +{ + PROP_0, + PROP_PEAK_ADJUSTMENT, + PROP_RMS_ADJUSTMENT, + PROP_SCALE, + PROP_ORIENTATION, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void gvc_level_bar_class_init (GvcLevelBarClass *klass); +static void gvc_level_bar_init (GvcLevelBar *bar); +static void gvc_level_bar_finalize (GObject *object); + +G_DEFINE_TYPE (GvcLevelBar, gvc_level_bar, GTK_TYPE_WIDGET) + +static gboolean +layout_changed (LevelBarLayout *layout1, LevelBarLayout *layout2) +{ + if (layout1->area.x != layout2->area.x) + return TRUE; + if (layout1->area.y != layout2->area.y) + return TRUE; + if (layout1->area.width != layout2->area.width) + return TRUE; + if (layout1->area.height != layout2->area.height) + return TRUE; + + if (layout1->delta != layout2->delta) + return TRUE; + if (layout1->peak_num != layout2->peak_num) + return TRUE; + if (layout1->max_peak_num != layout2->max_peak_num) + return TRUE; + +#if GTK_CHECK_VERSION (3, 0, 0) + if (!gdk_rgba_equal (&layout1->color_fg, &layout2->color_fg)) + return TRUE; + if (!gdk_rgba_equal (&layout1->color_bg, &layout2->color_bg)) + return TRUE; + if (!gdk_rgba_equal (&layout1->color_dark, &layout2->color_dark)) + return TRUE; +#else + if (!gdk_color_equal (&layout1->color_fg, &layout2->color_fg)) + return TRUE; + if (!gdk_color_equal (&layout1->color_bg, &layout2->color_bg)) + return TRUE; + if (!gdk_color_equal (&layout1->color_dark, &layout2->color_dark)) + return TRUE; +#endif + return FALSE; +} + +static gdouble +fraction_from_adjustment (GvcLevelBar *bar, + GtkAdjustment *adjustment) +{ + gdouble level; + gdouble fraction = 0.0; + gdouble min; + gdouble max; + + level = gtk_adjustment_get_value (adjustment); + min = gtk_adjustment_get_lower (adjustment); + max = gtk_adjustment_get_upper (adjustment); + + switch (bar->priv->scale) { + case GVC_LEVEL_SCALE_LINEAR: + fraction = (level - min) / (max - min); + break; + case GVC_LEVEL_SCALE_LOG: + fraction = log10 ((level - min + 1) / (max - min + 1)); + break; + } + + return fraction; +} + +static gboolean +reset_max_peak (GvcLevelBar *bar) +{ + bar->priv->max_peak = gtk_adjustment_get_lower (bar->priv->peak_adjustment); + + bar->priv->layout.max_peak_num = 0; + + gtk_widget_queue_draw (GTK_WIDGET (bar)); + + bar->priv->max_peak_id = 0; + return FALSE; +} + +static void +bar_calc_layout (GvcLevelBar *bar) +{ + int peak_level; + int max_peak_level; + GtkAllocation allocation; + +#if GTK_CHECK_VERSION (3, 0, 0) + GtkStyleContext *context; + + context = gtk_widget_get_style_context (GTK_WIDGET (bar)); + + gtk_style_context_get_background_color (context, + GTK_STATE_FLAG_NORMAL, + &bar->priv->layout.color_bg); + gtk_style_context_get_background_color (context, + GTK_STATE_FLAG_SELECTED, + &bar->priv->layout.color_fg); + gtk_style_context_get_color (context, + GTK_STATE_FLAG_NORMAL, + &bar->priv->layout.color_dark); + + gvc_color_shade (&bar->priv->layout.color_dark, + &bar->priv->layout.color_dark, + 0.7); +#else + GtkStyle *style; + + style = gtk_widget_get_style (GTK_WIDGET (bar)); + + bar->priv->layout.color_bg = style->bg[GTK_STATE_NORMAL]; + bar->priv->layout.color_fg = style->bg[GTK_STATE_SELECTED]; + bar->priv->layout.color_dark = style->dark[GTK_STATE_NORMAL]; +#endif + + gtk_widget_get_allocation (GTK_WIDGET (bar), &allocation); + + bar->priv->layout.area.width = allocation.width - 2; + bar->priv->layout.area.height = allocation.height - 2; + + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + peak_level = bar->priv->peak_fraction * bar->priv->layout.area.height; + max_peak_level = bar->priv->max_peak * bar->priv->layout.area.height; + + bar->priv->layout.delta = bar->priv->layout.area.height / NUM_BOXES; + bar->priv->layout.area.x = 0; + bar->priv->layout.area.y = 0; + bar->priv->layout.box_height = bar->priv->layout.delta / 2; + bar->priv->layout.box_width = bar->priv->layout.area.width; + bar->priv->layout.box_radius = bar->priv->layout.box_width / 2; + } else { + peak_level = bar->priv->peak_fraction * bar->priv->layout.area.width; + max_peak_level = bar->priv->max_peak * bar->priv->layout.area.width; + + bar->priv->layout.delta = bar->priv->layout.area.width / NUM_BOXES; + bar->priv->layout.area.x = 0; + bar->priv->layout.area.y = 0; + bar->priv->layout.box_width = bar->priv->layout.delta / 2; + bar->priv->layout.box_height = bar->priv->layout.area.height; + bar->priv->layout.box_radius = bar->priv->layout.box_height / 2; + } + + bar->priv->layout.peak_num = peak_level / bar->priv->layout.delta; + bar->priv->layout.max_peak_num = max_peak_level / bar->priv->layout.delta; +} + +static void +update_peak_value (GvcLevelBar *bar) +{ + gdouble value; + LevelBarLayout layout; + + value = fraction_from_adjustment (bar, bar->priv->peak_adjustment); + + bar->priv->peak_fraction = value; + + if (value > bar->priv->max_peak) { + if (bar->priv->max_peak_id > 0) + g_source_remove (bar->priv->max_peak_id); + + bar->priv->max_peak_id = + g_timeout_add_seconds (1, (GSourceFunc) reset_max_peak, bar); + bar->priv->max_peak = value; + } + + layout = bar->priv->layout; + + bar_calc_layout (bar); + + if (layout_changed (&bar->priv->layout, &layout)) + gtk_widget_queue_draw (GTK_WIDGET (bar)); +} + +static void +update_rms_value (GvcLevelBar *bar) +{ + bar->priv->rms_fraction = fraction_from_adjustment (bar, bar->priv->rms_adjustment); +} + +GtkOrientation +gvc_level_bar_get_orientation (GvcLevelBar *bar) +{ + g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), 0); + + return bar->priv->orientation; +} + +void +gvc_level_bar_set_orientation (GvcLevelBar *bar, + GtkOrientation orientation) +{ + g_return_if_fail (GVC_IS_LEVEL_BAR (bar)); + + if (orientation != bar->priv->orientation) { + if (G_UNLIKELY (orientation != GTK_ORIENTATION_VERTICAL && + orientation != GTK_ORIENTATION_HORIZONTAL)) { + g_warn_if_reached (); + return; + } + + bar->priv->orientation = orientation; + + gtk_widget_queue_draw (GTK_WIDGET (bar)); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]); + } +} + +static void +on_peak_adjustment_value_changed (GtkAdjustment *adjustment, + GvcLevelBar *bar) +{ + update_peak_value (bar); +} + +static void +on_rms_adjustment_value_changed (GtkAdjustment *adjustment, + GvcLevelBar *bar) +{ + update_rms_value (bar); +} + +void +gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar, + GtkAdjustment *adjustment) +{ + g_return_if_fail (GVC_LEVEL_BAR (bar)); + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + + if (bar->priv->peak_adjustment != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->peak_adjustment), + G_CALLBACK (on_peak_adjustment_value_changed), + bar); + g_object_unref (bar->priv->peak_adjustment); + } + + bar->priv->peak_adjustment = g_object_ref_sink (adjustment); + + g_signal_connect (G_OBJECT (bar->priv->peak_adjustment), + "value-changed", + G_CALLBACK (on_peak_adjustment_value_changed), + bar); + + update_peak_value (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_PEAK_ADJUSTMENT]); +} + +void +gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar, + GtkAdjustment *adjustment) +{ + g_return_if_fail (GVC_LEVEL_BAR (bar)); + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + + if (bar->priv->rms_adjustment != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->rms_adjustment), + G_CALLBACK (on_rms_adjustment_value_changed), + bar); + g_object_unref (bar->priv->rms_adjustment); + } + + bar->priv->rms_adjustment = g_object_ref_sink (adjustment); + + g_signal_connect (G_OBJECT (bar->priv->rms_adjustment), + "value-changed", + G_CALLBACK (on_rms_adjustment_value_changed), + bar); + + update_rms_value (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_RMS_ADJUSTMENT]); +} + +GtkAdjustment * +gvc_level_bar_get_peak_adjustment (GvcLevelBar *bar) +{ + g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), NULL); + + return bar->priv->peak_adjustment; +} + +GtkAdjustment * +gvc_level_bar_get_rms_adjustment (GvcLevelBar *bar) +{ + g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), NULL); + + return bar->priv->rms_adjustment; +} + +void +gvc_level_bar_set_scale (GvcLevelBar *bar, GvcLevelScale scale) +{ + g_return_if_fail (GVC_IS_LEVEL_BAR (bar)); + + if (scale != bar->priv->scale) { + if (G_UNLIKELY (scale != GVC_LEVEL_SCALE_LINEAR && + scale != GVC_LEVEL_SCALE_LOG)) { + g_warn_if_reached (); + return; + } + bar->priv->scale = scale; + + update_peak_value (bar); + update_rms_value (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SCALE]); + } +} + +static void +gvc_level_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GvcLevelBar *self = GVC_LEVEL_BAR (object); + + switch (prop_id) { + case PROP_SCALE: + gvc_level_bar_set_scale (self, g_value_get_int (value)); + break; + case PROP_ORIENTATION: + gvc_level_bar_set_orientation (self, g_value_get_enum (value)); + break; + case PROP_PEAK_ADJUSTMENT: + gvc_level_bar_set_peak_adjustment (self, g_value_get_object (value)); + break; + case PROP_RMS_ADJUSTMENT: + gvc_level_bar_set_rms_adjustment (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_level_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GvcLevelBar *self = GVC_LEVEL_BAR (object); + + switch (prop_id) { + case PROP_SCALE: + g_value_set_int (value, self->priv->scale); + break; + case PROP_ORIENTATION: + g_value_set_enum (value, self->priv->orientation); + break; + case PROP_PEAK_ADJUSTMENT: + g_value_set_object (value, self->priv->peak_adjustment); + break; + case PROP_RMS_ADJUSTMENT: + g_value_set_object (value, self->priv->rms_adjustment); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_level_bar_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GvcLevelBar *bar; + + g_return_if_fail (GVC_IS_LEVEL_BAR (widget)); + g_return_if_fail (requisition != NULL); + + bar = GVC_LEVEL_BAR (widget); + + switch (bar->priv->orientation) { + case GTK_ORIENTATION_VERTICAL: + requisition->width = VERTICAL_BAR_WIDTH; + requisition->height = MIN_VERTICAL_BAR_HEIGHT; + break; + case GTK_ORIENTATION_HORIZONTAL: + requisition->width = MIN_HORIZONTAL_BAR_WIDTH; + requisition->height = HORIZONTAL_BAR_HEIGHT; + break; + } +} + +#if GTK_CHECK_VERSION (3, 0, 0) +static void +gvc_level_bar_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkRequisition requisition; + + gvc_level_bar_size_request (widget, &requisition); + + if (minimum != NULL) + *minimum = requisition.width; + if (natural != NULL) + *natural = requisition.width; +} + +static void +gvc_level_bar_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkRequisition requisition; + + gvc_level_bar_size_request (widget, &requisition); + + if (minimum != NULL) + *minimum = requisition.height; + if (natural != NULL) + *natural = requisition.height; +} +#endif + +static void +gvc_level_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation) +{ + GvcLevelBar *bar; + + bar = GVC_LEVEL_BAR (widget); + + /* FIXME: add height property, labels, etc */ + GTK_WIDGET_CLASS (gvc_level_bar_parent_class)->size_allocate (widget, allocation); + + gtk_widget_set_allocation (widget, allocation); + gtk_widget_get_allocation (widget, allocation); + + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + allocation->height = MIN (allocation->height, MIN_VERTICAL_BAR_HEIGHT); + allocation->width = MAX (allocation->width, VERTICAL_BAR_WIDTH); + } else { + allocation->width = MIN (allocation->width, MIN_HORIZONTAL_BAR_WIDTH); + allocation->height = MAX (allocation->height, HORIZONTAL_BAR_HEIGHT); + } + + bar_calc_layout (bar); +} + +static void +curved_rectangle (cairo_t *cr, + double x0, + double y0, + double width, + double height, + double radius) +{ + double x1; + double y1; + + x1 = x0 + width; + y1 = y0 + height; + + if (!width || !height) + return; + + if (width / 2 < radius) { + if (height / 2 < radius) { + cairo_move_to (cr, x0, (y0 + y1) / 2); + cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0); + cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); + cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); + cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); + } else { + cairo_move_to (cr, x0, y0 + radius); + cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0); + cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); + cairo_line_to (cr, x1, y1 - radius); + cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); + cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); + } + } else { + if (height / 2 < radius) { + cairo_move_to (cr, x0, (y0 + y1) / 2); + cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0); + cairo_line_to (cr, x1 - radius, y0); + cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); + cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); + cairo_line_to (cr, x0 + radius, y1); + cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); + } else { + cairo_move_to (cr, x0, y0 + radius); + cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0); + cairo_line_to (cr, x1 - radius, y0); + cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); + cairo_line_to (cr, x1, y1 - radius); + cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); + cairo_line_to (cr, x0 + radius, y1); + cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); + } + } + + cairo_close_path (cr); +} + +static int +gvc_level_bar_draw (GtkWidget *widget, cairo_t *cr) +{ + GvcLevelBar *bar; + + bar = GVC_LEVEL_BAR (widget); + + cairo_save (cr); + + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + int i; + int by; + + for (i = 0; i < NUM_BOXES; i++) { + by = i * bar->priv->layout.delta; + curved_rectangle (cr, + bar->priv->layout.area.x + 0.5, + by + 0.5, + bar->priv->layout.box_width - 1, + bar->priv->layout.box_height - 1, + bar->priv->layout.box_radius); + if ((bar->priv->layout.max_peak_num - 1) == i) { + /* fill peak foreground */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg); +#endif + cairo_fill_preserve (cr); + } else if ((bar->priv->layout.peak_num - 1) >= i) { + /* fill background */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif + cairo_fill_preserve (cr); + + /* fill foreground */ +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red, + bar->priv->layout.color_fg.green, + bar->priv->layout.color_fg.blue, + 0.5); +#else + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red / 65535.0, + bar->priv->layout.color_fg.green / 65535.0, + bar->priv->layout.color_fg.blue / 65535.0, + 0.5); +#endif + cairo_fill_preserve (cr); + } else { + /* fill background */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif + cairo_fill_preserve (cr); + } + + /* stroke border */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark); +#endif + cairo_set_line_width (cr, 1); + cairo_stroke (cr); + } + } else { + int i; + int bx; + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) { + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + + cairo_scale (cr, -1, 1); + cairo_translate (cr, -allocation.width, 0); + } + + for (i = 0; i < NUM_BOXES; i++) { + bx = i * bar->priv->layout.delta; + curved_rectangle (cr, + bx + 0.5, + bar->priv->layout.area.y + 0.5, + bar->priv->layout.box_width - 1, + bar->priv->layout.box_height - 1, + bar->priv->layout.box_radius); + + if ((bar->priv->layout.max_peak_num - 1) == i) { + /* fill peak foreground */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg); +#endif + cairo_fill_preserve (cr); + } else if ((bar->priv->layout.peak_num - 1) >= i) { + /* fill background */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif + cairo_fill_preserve (cr); + + /* fill foreground */ +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red, + bar->priv->layout.color_fg.green, + bar->priv->layout.color_fg.blue, + 0.5); +#else + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red / 65535.0, + bar->priv->layout.color_fg.green / 65535.0, + bar->priv->layout.color_fg.blue / 65535.0, + 0.5); +#endif + cairo_fill_preserve (cr); + } else { + /* fill background */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif + cairo_fill_preserve (cr); + } + + /* stroke border */ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark); +#endif + cairo_set_line_width (cr, 1); + cairo_stroke (cr); + } + } + + cairo_restore (cr); + + return FALSE; +} + +#if !GTK_CHECK_VERSION (3, 0, 0) +static int +gvc_level_bar_expose (GtkWidget *widget, GdkEventExpose *event) +{ + cairo_t *cr; + GtkAllocation allocation; + + g_return_val_if_fail (event != NULL, FALSE); + + /* Event queue compression */ + if (event->count > 0) + return FALSE; + + gtk_widget_get_allocation (widget, &allocation); + + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + cairo_translate (cr, + allocation.x, + allocation.y); + + gvc_level_bar_draw (widget, cr); + + cairo_destroy (cr); + return FALSE; +} +#endif + +static void +gvc_level_bar_class_init (GvcLevelBarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = gvc_level_bar_finalize; + object_class->set_property = gvc_level_bar_set_property; + object_class->get_property = gvc_level_bar_get_property; + +#if GTK_CHECK_VERSION (3, 0, 0) + widget_class->draw = gvc_level_bar_draw; + widget_class->get_preferred_width = gvc_level_bar_get_preferred_width; + widget_class->get_preferred_height = gvc_level_bar_get_preferred_height; +#else + widget_class->expose_event = gvc_level_bar_expose; + widget_class->size_request = gvc_level_bar_size_request; +#endif + widget_class->size_allocate = gvc_level_bar_size_allocate; + + properties[PROP_ORIENTATION] = + g_param_spec_enum ("orientation", + "Orientation", + "The orientation of the bar", + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_PEAK_ADJUSTMENT] = + g_param_spec_object ("peak-adjustment", + "Peak Adjustment", + "The GtkAdjustment that contains the current peak value", + GTK_TYPE_ADJUSTMENT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_RMS_ADJUSTMENT] = + g_param_spec_object ("rms-adjustment", + "RMS Adjustment", + "The GtkAdjustment that contains the current rms value", + GTK_TYPE_ADJUSTMENT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SCALE] = + g_param_spec_int ("scale", + "Scale", + "Scale", + 0, + G_MAXINT, + GVC_LEVEL_SCALE_LINEAR, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (klass, sizeof (GvcLevelBarPrivate)); +} + +static void +gvc_level_bar_init (GvcLevelBar *bar) +{ + bar->priv = GVC_LEVEL_BAR_GET_PRIVATE (bar); + + bar->priv->peak_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, + 0.0, + 1.0, + 0.05, + 0.1, + 0.1)); + g_object_ref_sink (bar->priv->peak_adjustment); + + g_signal_connect (bar->priv->peak_adjustment, + "value-changed", + G_CALLBACK (on_peak_adjustment_value_changed), + bar); + + bar->priv->rms_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, + 0.0, + 1.0, + 0.05, + 0.1, + 0.1)); + g_object_ref_sink (bar->priv->rms_adjustment); + + g_signal_connect (bar->priv->rms_adjustment, + "value-changed", + G_CALLBACK (on_rms_adjustment_value_changed), + bar); + + gtk_widget_set_has_window (GTK_WIDGET (bar), FALSE); +} + +static void +gvc_level_bar_finalize (GObject *object) +{ + GvcLevelBar *bar; + + bar = GVC_LEVEL_BAR (object); + + if (bar->priv->max_peak_id > 0) + g_source_remove (bar->priv->max_peak_id); + + G_OBJECT_CLASS (gvc_level_bar_parent_class)->finalize (object); +} + +GtkWidget * +gvc_level_bar_new (void) +{ + return g_object_new (GVC_TYPE_LEVEL_BAR, NULL); +} diff --git a/mate-volume-control/gvc-level-bar.h b/mate-volume-control/gvc-level-bar.h new file mode 100644 index 0000000..ef9ae7e --- /dev/null +++ b/mate-volume-control/gvc-level-bar.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_LEVEL_BAR_H +#define __GVC_LEVEL_BAR_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define GVC_TYPE_LEVEL_BAR (gvc_level_bar_get_type ()) +#define GVC_LEVEL_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBar)) +#define GVC_LEVEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_LEVEL_BAR, GvcLevelBarClass)) +#define GVC_IS_LEVEL_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_LEVEL_BAR)) +#define GVC_IS_LEVEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_LEVEL_BAR)) +#define GVC_LEVEL_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBarClass)) + +typedef struct _GvcLevelBar GvcLevelBar; +typedef struct _GvcLevelBarClass GvcLevelBarClass; +typedef struct _GvcLevelBarPrivate GvcLevelBarPrivate; + +struct _GvcLevelBar +{ + GtkWidget parent; + GvcLevelBarPrivate *priv; +}; + +struct _GvcLevelBarClass +{ + GtkWidgetClass parent_class; +}; + +typedef enum +{ + GVC_LEVEL_SCALE_LINEAR, + GVC_LEVEL_SCALE_LOG +} GvcLevelScale; + +GType gvc_level_bar_get_type (void) G_GNUC_CONST; + +GtkWidget * gvc_level_bar_new (void); +void gvc_level_bar_set_orientation (GvcLevelBar *bar, + GtkOrientation orientation); +GtkOrientation gvc_level_bar_get_orientation (GvcLevelBar *bar); + +void gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar, + GtkAdjustment *adjustment); +GtkAdjustment * gvc_level_bar_get_peak_adjustment (GvcLevelBar *bar); + +void gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar, + GtkAdjustment *adjustment); +GtkAdjustment * gvc_level_bar_get_rms_adjustment (GvcLevelBar *bar); + +void gvc_level_bar_set_scale (GvcLevelBar *bar, + GvcLevelScale scale); + +G_END_DECLS + +#endif /* __GVC_LEVEL_BAR_H */ diff --git a/mate-volume-control/gvc-mixer-dialog.c b/mate-volume-control/gvc-mixer-dialog.c new file mode 100644 index 0000000..b6525fb --- /dev/null +++ b/mate-volume-control/gvc-mixer-dialog.c @@ -0,0 +1,2606 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gvc-channel-bar.h" +#include "gvc-balance-bar.h" +#include "gvc-combo-box.h" +#include "gvc-mixer-dialog.h" +#include "gvc-sound-theme-chooser.h" +#include "gvc-level-bar.h" +#include "gvc-speaker-test.h" +#include "gvc-utils.h" + +#define GVC_MIXER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogPrivate)) + +struct _GvcMixerDialogPrivate +{ + MateMixerContext *context; + GHashTable *bars; + GtkWidget *notebook; + GtkWidget *output_bar; + GtkWidget *input_bar; + GtkWidget *input_level_bar; + GtkWidget *effects_bar; + GtkWidget *output_stream_box; + GtkWidget *sound_effects_box; + GtkWidget *hw_box; + GtkWidget *hw_treeview; + GtkWidget *hw_settings_box; + GtkWidget *hw_profile_combo; + GtkWidget *input_box; + GtkWidget *output_box; + GtkWidget *applications_box; + GtkWidget *applications_window; + GtkWidget *no_apps_label; + GtkWidget *output_treeview; + GtkWidget *output_settings_frame; + GtkWidget *output_settings_box; + GtkWidget *output_balance_bar; + GtkWidget *output_fade_bar; + GtkWidget *output_lfe_bar; + GtkWidget *output_port_combo; + GtkWidget *input_treeview; + GtkWidget *input_port_combo; + GtkWidget *input_settings_box; + GtkWidget *sound_theme_chooser; + GtkSizeGroup *size_group; + gdouble last_input_peak; + guint num_apps; +}; + +enum { + ICON_COLUMN, + NAME_COLUMN, + LABEL_COLUMN, + ACTIVE_COLUMN, + SPEAKERS_COLUMN, + NUM_COLUMNS +}; + +enum { + HW_ICON_COLUMN, + HW_NAME_COLUMN, + HW_LABEL_COLUMN, + HW_STATUS_COLUMN, + HW_PROFILE_COLUMN, + HW_NUM_COLUMNS +}; + +enum { + PAGE_EFFECTS, + PAGE_HARDWARE, + PAGE_INPUT, + PAGE_OUTPUT, + PAGE_APPLICATIONS +}; + +enum { + PROP_0, + PROP_CONTEXT +}; + +static const guint tab_accel_keys[] = { + GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5 +}; + +static void gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass); +static void gvc_mixer_dialog_init (GvcMixerDialog *dialog); +static void gvc_mixer_dialog_finalize (GObject *object); + +static void add_stream (GvcMixerDialog *dialog, + MateMixerStream *stream); +static void add_application_control (GvcMixerDialog *dialog, + MateMixerStreamControl *control); +static void add_effects_control (GvcMixerDialog *dialog, + MateMixerStreamControl *control); + +static void remove_stream (GvcMixerDialog *dialog, + const gchar *name); +static void remove_application_control (GvcMixerDialog *dialog, + const gchar *name); + +static void bar_set_stream (GvcMixerDialog *dialog, + GtkWidget *bar, + MateMixerStream *stream); +static void bar_set_stream_control (GvcMixerDialog *dialog, + GtkWidget *bar, + MateMixerStreamControl *control); + +G_DEFINE_TYPE (GvcMixerDialog, gvc_mixer_dialog, GTK_TYPE_DIALOG) + +static MateMixerSwitch * +find_stream_port_switch (MateMixerStream *stream) +{ + const GList *switches; + + switches = mate_mixer_stream_list_switches (stream); + while (switches != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (switches->data); + MateMixerSwitchFlags flags; + + flags = mate_mixer_switch_get_flags (swtch); + if ((flags & MATE_MIXER_SWITCH_TOGGLE) == 0 && + mate_mixer_switch_get_role (swtch) == MATE_MIXER_SWITCH_ROLE_PORT) + return swtch; + + switches = switches->next; + } + return NULL; +} + +static MateMixerSwitch * +find_device_profile_switch (MateMixerDevice *device) +{ + const GList *switches; + + switches = mate_mixer_device_list_switches (device); + while (switches != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (switches->data); + + if (mate_mixer_switch_get_role (swtch) == MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE) + return swtch; + + switches = switches->next; + } + return NULL; +} + +static MateMixerStream * +find_device_test_stream (GvcMixerDialog *dialog, MateMixerDevice *device) +{ + const GList *streams; + + streams = mate_mixer_device_list_streams (device); + while (streams != NULL) { + MateMixerStream *stream; + MateMixerDirection direction; + + stream = MATE_MIXER_STREAM (streams->data); + direction = mate_mixer_stream_get_direction (stream); + + if (direction == MATE_MIXER_DIRECTION_OUTPUT) { + MateMixerStreamControl *control; + + control = mate_mixer_stream_get_default_control (stream); + if (mate_mixer_stream_control_get_num_channels (control) > 0) + return stream; + } + streams = streams->next; + } + return FALSE; +} + +static gboolean +find_tree_item_by_name (GtkTreeModel *model, + const gchar *name, + guint column, + GtkTreeIter *iter) +{ + gboolean found = FALSE; + + if (!gtk_tree_model_get_iter_first (model, iter)) + return FALSE; + + do { + gchar *n; + gtk_tree_model_get (model, iter, column, &n, -1); + + if (!g_strcmp0 (name, n)) + found = TRUE; + + g_free (n); + } while (!found && gtk_tree_model_iter_next (model, iter)); + + return found; +} + +static void +update_default_tree_item (GvcMixerDialog *dialog, + GtkTreeModel *model, + MateMixerStream *stream) +{ + GtkTreeIter iter; + const gchar *name = NULL; + + if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) + return; + + /* The supplied stream is the default, or the selected item. Traverse + * the item list and mark each item as being selected or not. Also do not + * presume some known stream is selected and allow NULL here. */ + if (stream != NULL) + name = mate_mixer_stream_get_name (stream); + + do { + gchar *n; + gtk_tree_model_get (model, &iter, + NAME_COLUMN, &n, + -1); + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + ACTIVE_COLUMN, !g_strcmp0 (name, n), + -1); + g_free (n); + } while (gtk_tree_model_iter_next (model, &iter)); +} + +static void +on_combobox_option_changed (GvcComboBox *combo, + const gchar *name, + GvcMixerDialog *dialog) +{ + MateMixerSwitch *swtch; + MateMixerSwitchOption *option; + const GList *options; + + swtch = g_object_get_data (G_OBJECT (combo), "switch"); + if (G_UNLIKELY (swtch == NULL)) { + g_warn_if_reached (); + return; + } + + options = mate_mixer_switch_list_options (swtch); + while (options != NULL) { + option = MATE_MIXER_SWITCH_OPTION (options->data); + + if (g_strcmp0 (mate_mixer_switch_option_get_name (option), name) == 0) + break; + + option = NULL; + options = options->next; + } + + if (G_UNLIKELY (option == NULL)) { + g_warn_if_reached (); + return; + } + + mate_mixer_switch_set_active_option (swtch, option); +} + +static GtkWidget * +create_port_combo_box (GvcMixerDialog *dialog, + MateMixerSwitch *swtch, + const gchar *name, + const GList *items, + const gchar *active) +{ + GtkWidget *combobox; + + combobox = gvc_combo_box_new (name); + + gvc_combo_box_set_options (GVC_COMBO_BOX (combobox), items); + gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), active); + + gvc_combo_box_set_size_group (GVC_COMBO_BOX (combobox), + dialog->priv->size_group, + FALSE); + + g_object_set_data_full (G_OBJECT (combobox), + "switch", + g_object_ref (swtch), + g_object_unref); + + g_signal_connect (G_OBJECT (combobox), + "changed", + G_CALLBACK (on_combobox_option_changed), + dialog); + return combobox; +} + +static void +update_output_settings (GvcMixerDialog *dialog) +{ + MateMixerStream *stream; + MateMixerStreamControl *control; + MateMixerStreamControlFlags flags; + MateMixerSwitch *port_switch; + gboolean has_settings = FALSE; + + g_debug ("Updating output settings"); + + if (dialog->priv->output_balance_bar != NULL) { + gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), + dialog->priv->output_balance_bar); + + dialog->priv->output_balance_bar = NULL; + } + if (dialog->priv->output_fade_bar != NULL) { + gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), + dialog->priv->output_fade_bar); + + dialog->priv->output_fade_bar = NULL; + } + if (dialog->priv->output_lfe_bar != NULL) { + gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), + dialog->priv->output_lfe_bar); + + dialog->priv->output_lfe_bar = NULL; + } + if (dialog->priv->output_port_combo != NULL) { + gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), + dialog->priv->output_port_combo); + + dialog->priv->output_port_combo = NULL; + } + + /* Get the control currently associated with the output slider */ + control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->output_bar)); + if (control == NULL) { + g_debug ("There is no control for the default output stream"); + gtk_widget_hide (dialog->priv->output_settings_frame); + return; + } + flags = mate_mixer_stream_control_get_flags (control); + + /* Enable balance bar if it is available */ + if (flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE) { + dialog->priv->output_balance_bar = + gvc_balance_bar_new (control, BALANCE_TYPE_RL); + + gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_balance_bar), + dialog->priv->size_group, + TRUE); + + gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), + dialog->priv->output_balance_bar, + FALSE, FALSE, 6); + + gtk_widget_show (dialog->priv->output_balance_bar); + has_settings = TRUE; + } + + /* Enable fade bar if it is available */ + if (flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE) { + dialog->priv->output_fade_bar = + gvc_balance_bar_new (control, BALANCE_TYPE_FR); + + gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_fade_bar), + dialog->priv->size_group, + TRUE); + + gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), + dialog->priv->output_fade_bar, + FALSE, FALSE, 6); + + gtk_widget_show (dialog->priv->output_fade_bar); + has_settings = TRUE; + } + + /* Enable subwoofer volume bar if subwoofer is available */ + if (mate_mixer_stream_control_has_channel_position (control, MATE_MIXER_CHANNEL_LFE)) { + dialog->priv->output_lfe_bar = + gvc_balance_bar_new (control, BALANCE_TYPE_LFE); + + gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_lfe_bar), + dialog->priv->size_group, + TRUE); + + gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), + dialog->priv->output_lfe_bar, + FALSE, FALSE, 6); + + gtk_widget_show (dialog->priv->output_lfe_bar); + has_settings = TRUE; + } + + /* Get owning stream of the control */ + stream = mate_mixer_stream_control_get_stream (control); + if (G_UNLIKELY (stream == NULL)) + return; + + /* Enable the port selector if the stream has one */ + port_switch = find_stream_port_switch (stream); + if (port_switch != NULL) { + const GList *options; + const gchar *active_name = NULL; + MateMixerSwitchOption *active; + + options = mate_mixer_switch_list_options (port_switch); + active = mate_mixer_switch_get_active_option (port_switch); + if (active != NULL) + active_name = mate_mixer_switch_option_get_name (active); + + dialog->priv->output_port_combo = + create_port_combo_box (dialog, + port_switch, + _("Co_nnector:"), + options, + active_name); + + gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), + dialog->priv->output_port_combo, + TRUE, FALSE, 6); + + gtk_widget_show (dialog->priv->output_port_combo); + has_settings = TRUE; + } + + if (has_settings == TRUE) + gtk_widget_show (dialog->priv->output_settings_frame); + else + gtk_widget_hide (dialog->priv->output_settings_frame); +} + +static void +set_output_stream (GvcMixerDialog *dialog, MateMixerStream *stream) +{ + GtkTreeModel *model; + MateMixerSwitch *swtch; + MateMixerStreamControl *control; + + control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->output_bar)); + if (control != NULL) { + /* Disconnect port switch of the previous stream */ + if (dialog->priv->output_port_combo != NULL) { + swtch = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo), + "switch"); + if (swtch != NULL) + g_signal_handlers_disconnect_by_data (G_OBJECT (swtch), + dialog); + } + } + + bar_set_stream (dialog, dialog->priv->output_bar, stream); + + if (stream != NULL) { + const GList *controls; + + controls = mate_mixer_context_list_stored_controls (dialog->priv->context); + + /* Move all stored controls to the newly selected default stream */ + while (controls != NULL) { + MateMixerStream *parent; + MateMixerStreamControl *control; + + control = MATE_MIXER_STREAM_CONTROL (controls->data); + parent = mate_mixer_stream_control_get_stream (control); + + /* Prefer streamless controls to stay the way they are, forcing them to + * a particular owning stream would be wrong for eg. event controls */ + if (parent != NULL && parent != stream) { + MateMixerDirection direction = + mate_mixer_stream_get_direction (parent); + + if (direction == MATE_MIXER_DIRECTION_OUTPUT) + mate_mixer_stream_control_set_stream (control, stream); + } + controls = controls->next; + } + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); + update_default_tree_item (dialog, model, stream); + + update_output_settings (dialog); +} + +static void +on_context_default_output_stream_notify (MateMixerContext *context, + GParamSpec *pspec, + GvcMixerDialog *dialog) +{ + MateMixerStream *stream; + + stream = mate_mixer_context_get_default_output_stream (context); + + set_output_stream (dialog, stream); +} + +#define DECAY_STEP .15 + +static void +on_stream_control_monitor_value (MateMixerStream *stream, + gdouble value, + GvcMixerDialog *dialog) +{ + GtkAdjustment *adj; + + if (dialog->priv->last_input_peak >= DECAY_STEP) { + if (value < dialog->priv->last_input_peak - DECAY_STEP) { + value = dialog->priv->last_input_peak - DECAY_STEP; + } + } + + dialog->priv->last_input_peak = value; + + adj = gvc_level_bar_get_peak_adjustment (GVC_LEVEL_BAR (dialog->priv->input_level_bar)); + if (value >= 0) + gtk_adjustment_set_value (adj, value); + else + gtk_adjustment_set_value (adj, 0.0); +} + +static void +update_input_settings (GvcMixerDialog *dialog) +{ + MateMixerStream *stream; + MateMixerStreamControl *control; + MateMixerStreamControlFlags flags; + MateMixerSwitch *port_switch; + + g_debug ("Updating input settings"); + + if (dialog->priv->input_port_combo != NULL) { + gtk_container_remove (GTK_CONTAINER (dialog->priv->input_settings_box), + dialog->priv->input_port_combo); + + dialog->priv->input_port_combo = NULL; + } + + /* Get the control currently associated with the input slider */ + control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); + if (control == NULL) + return; + + flags = mate_mixer_stream_control_get_flags (control); + + /* Enable level bar only if supported by the control */ + if (flags & MATE_MIXER_STREAM_CONTROL_HAS_MONITOR) + g_signal_connect (G_OBJECT (control), + "monitor-value", + G_CALLBACK (on_stream_control_monitor_value), + dialog); + + /* Get owning stream of the control */ + stream = mate_mixer_stream_control_get_stream (control); + if (G_UNLIKELY (stream == NULL)) + return; + + /* Enable the port selector if the stream has one */ + port_switch = find_stream_port_switch (stream); + if (port_switch != NULL) { + const GList *options; + const gchar *active_name = NULL; + MateMixerSwitchOption *active; + + options = mate_mixer_switch_list_options (port_switch); + active = mate_mixer_switch_get_active_option (port_switch); + if (active != NULL) + active_name = mate_mixer_switch_option_get_name (active); + + dialog->priv->input_port_combo = + create_port_combo_box (dialog, + port_switch, + _("Co_nnector:"), + options, + active_name); + + gtk_box_pack_start (GTK_BOX (dialog->priv->input_settings_box), + dialog->priv->input_port_combo, + TRUE, TRUE, 0); + + gtk_widget_show (dialog->priv->input_port_combo); + } +} + +static void +on_stream_control_mute_notify (MateMixerStreamControl *control, + GParamSpec *pspec, + GvcMixerDialog *dialog) +{ + /* Stop monitoring the input stream when it gets muted */ + if (mate_mixer_stream_control_get_mute (control) == TRUE) + mate_mixer_stream_control_set_monitor_enabled (control, FALSE); + else + mate_mixer_stream_control_set_monitor_enabled (control, TRUE); +} + +static void +set_input_stream (GvcMixerDialog *dialog, MateMixerStream *stream) +{ + GtkTreeModel *model; + MateMixerSwitch *swtch; + MateMixerStreamControl *control; + + control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); + if (control != NULL) { + /* Disconnect port switch of the previous stream */ + if (dialog->priv->input_port_combo != NULL) { + swtch = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo), + "switch"); + if (swtch != NULL) + g_signal_handlers_disconnect_by_data (G_OBJECT (swtch), + dialog); + } + + /* Disable monitoring of the previous control */ + g_signal_handlers_disconnect_by_func (G_OBJECT (control), + G_CALLBACK (on_stream_control_monitor_value), + dialog); + + mate_mixer_stream_control_set_monitor_enabled (control, FALSE); + } + + bar_set_stream (dialog, dialog->priv->input_bar, stream); + + if (stream != NULL) { + const GList *controls; + guint page = gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog->priv->notebook)); + + controls = mate_mixer_context_list_stored_controls (dialog->priv->context); + + /* Move all stored controls to the newly selected default stream */ + while (controls != NULL) { + MateMixerStream *parent; + + control = MATE_MIXER_STREAM_CONTROL (controls->data); + parent = mate_mixer_stream_control_get_stream (control); + + /* Prefer streamless controls to stay the way they are, forcing them to + * a particular owning stream would be wrong for eg. event controls */ + if (parent != NULL && parent != stream) { + MateMixerDirection direction = + mate_mixer_stream_get_direction (parent); + + if (direction == MATE_MIXER_DIRECTION_INPUT) + mate_mixer_stream_control_set_stream (control, stream); + } + controls = controls->next; + } + + if (page == PAGE_INPUT) { + MateMixerStreamControl *control = + gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); + + if (G_LIKELY (control != NULL)) + mate_mixer_stream_control_set_monitor_enabled (control, TRUE); + } + + /* Enable/disable the peak level monitor according to mute state */ + g_signal_connect (G_OBJECT (stream), + "notify::mute", + G_CALLBACK (on_stream_control_mute_notify), + dialog); + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); + update_default_tree_item (dialog, model, stream); + + update_input_settings (dialog); +} + +static void +on_context_default_input_stream_notify (MateMixerContext *context, + GParamSpec *pspec, + GvcMixerDialog *dialog) +{ + MateMixerStream *stream; + + g_debug ("Default input stream has changed"); + + stream = mate_mixer_context_get_default_input_stream (context); + + set_input_stream (dialog, stream); +} + +static GvcComboBox * +find_combo_box_by_switch (GvcMixerDialog *dialog, MateMixerSwitch *swtch) +{ + MateMixerSwitch *combo_switch; + + if (dialog->priv->output_port_combo != NULL) { + combo_switch = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo), + "switch"); + if (combo_switch == swtch) + return GVC_COMBO_BOX (dialog->priv->output_port_combo); + } + + if (dialog->priv->input_port_combo != NULL) { + combo_switch = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo), + "switch"); + if (combo_switch == swtch) + return GVC_COMBO_BOX (dialog->priv->input_port_combo); + } + return NULL; +} + +static void +on_switch_option_notify (MateMixerSwitch *swtch, + GParamSpec *pspec, + GvcMixerDialog *dialog) +{ + GvcComboBox *combobox; + MateMixerSwitchOption *port; + + combobox = find_combo_box_by_switch (dialog, swtch); + if (G_UNLIKELY (combobox == NULL)) { + g_warn_if_reached (); + return; + } + + g_signal_handlers_block_by_func (G_OBJECT (combobox), + on_combobox_option_changed, + dialog); + + port = mate_mixer_switch_get_active_option (swtch); + if (G_LIKELY (port != NULL)) + gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), + mate_mixer_switch_option_get_name (port)); + + g_signal_handlers_unblock_by_func (G_OBJECT (combobox), + on_combobox_option_changed, + dialog); +} + +static GtkWidget * +create_bar (GvcMixerDialog *dialog, gboolean use_size_group, gboolean symmetric) +{ + GtkWidget *bar; + + bar = gvc_channel_bar_new (NULL); + + if (use_size_group == TRUE) + gvc_channel_bar_set_size_group (GVC_CHANNEL_BAR (bar), + dialog->priv->size_group, + symmetric); + + g_object_set (G_OBJECT (bar), + "orientation", GTK_ORIENTATION_HORIZONTAL, + "show-mute", TRUE, + "show-icons", TRUE, + "show-marks", TRUE, + "extended", TRUE, NULL); + return bar; +} + +static void +bar_set_stream (GvcMixerDialog *dialog, + GtkWidget *bar, + MateMixerStream *stream) +{ + MateMixerStreamControl *control = NULL; + + if (stream != NULL) { + MateMixerSwitch *port_switch; + + control = mate_mixer_stream_get_default_control (stream); + + port_switch = find_stream_port_switch (stream); + if (port_switch != NULL) + g_signal_connect (G_OBJECT (port_switch), + "notify::active-option", + G_CALLBACK (on_switch_option_notify), + dialog); + } + + bar_set_stream_control (dialog, bar, control); +} + +static void +bar_set_stream_control (GvcMixerDialog *dialog, + GtkWidget *bar, + MateMixerStreamControl *control) +{ + MateMixerStreamControl *previous; + + previous = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (bar)); + if (previous != NULL) { + const gchar *name = mate_mixer_stream_control_get_name (previous); + + g_debug ("Disconnecting old stream control %s", name); + + g_signal_handlers_disconnect_by_data (G_OBJECT (previous), dialog); + + /* This may not do anything because we no longer have the information + * about the owning stream, in case it was an input stream, make + * sure to disconnected from the peak level monitor */ + mate_mixer_stream_control_set_monitor_enabled (previous, FALSE); + + g_hash_table_remove (dialog->priv->bars, name); + } + + gvc_channel_bar_set_control (GVC_CHANNEL_BAR (bar), control); + + if (control != NULL) { + const gchar *name = mate_mixer_stream_control_get_name (control); + + g_hash_table_insert (dialog->priv->bars, + (gpointer) name, + bar); + + gtk_widget_set_sensitive (GTK_WIDGET (bar), TRUE); + } else + gtk_widget_set_sensitive (GTK_WIDGET (bar), TRUE); +} + +static void +add_application_control (GvcMixerDialog *dialog, MateMixerStreamControl *control) +{ + MateMixerStream *stream; + MateMixerStreamControlMediaRole media_role; + MateMixerAppInfo *info; + MateMixerDirection direction = MATE_MIXER_DIRECTION_UNKNOWN; + GtkWidget *bar; + const gchar *app_id; + const gchar *app_name; + const gchar *app_icon; + + media_role = mate_mixer_stream_control_get_media_role (control); + + /* Add stream to the applications page, but make sure the stream qualifies + * for the inclusion */ + info = mate_mixer_stream_control_get_app_info (control); + if (info == NULL) + return; + + /* Skip streams with roles we don't care about */ + if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT || + media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST || + media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT || + media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER) + return; + + app_id = mate_mixer_app_info_get_id (info); + + /* These applications may have associated streams because they do peak + * level monitoring, skip these too */ + if (!g_strcmp0 (app_id, "org.mate.VolumeControl") || + !g_strcmp0 (app_id, "org.gnome.VolumeControl") || + !g_strcmp0 (app_id, "org.PulseAudio.pavucontrol")) + return; + + app_name = mate_mixer_app_info_get_name (info); + if (app_name == NULL) + app_name = mate_mixer_stream_control_get_label (control); + if (app_name == NULL) + app_name = mate_mixer_stream_control_get_name (control); + if (G_UNLIKELY (app_name == NULL)) + return; + + bar = create_bar (dialog, FALSE, FALSE); + + g_object_set (G_OBJECT (bar), + "show-marks", FALSE, + "extended", FALSE, + NULL); + + /* By default channel bars use speaker icons, use microphone icons + * instead for recording applications */ + stream = mate_mixer_stream_control_get_stream (control); + if (stream != NULL) + direction = mate_mixer_stream_get_direction (stream); + + if (direction == MATE_MIXER_DIRECTION_INPUT) + g_object_set (G_OBJECT (bar), + "low-icon-name", "audio-input-microphone-low", + "high-icon-name", "audio-input-microphone-high", + NULL); + + app_icon = mate_mixer_app_info_get_icon (info); + if (app_icon == NULL) { + if (direction == MATE_MIXER_DIRECTION_INPUT) + app_icon = "audio-input-microphone"; + else + app_icon = "applications-multimedia"; + } + + gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), app_name); + gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar), app_icon); + + gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), + bar, + FALSE, FALSE, 12); + + bar_set_stream_control (dialog, bar, control); + dialog->priv->num_apps++; + + gtk_widget_hide (dialog->priv->no_apps_label); + gtk_widget_show (bar); +} + +static void +add_effects_control (GvcMixerDialog *dialog, MateMixerStreamControl *control) +{ + MateMixerStreamControl *previous; + const gchar *name; + + /* We use a stored event control for the effects volume slider, + * because regular streams are only created when an event sound + * is played and then immediately destroyed. + * The stored event control should exist all the time. */ + previous = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->effects_bar)); + if (previous != NULL) { + name = mate_mixer_stream_control_get_name (previous); + + g_signal_handlers_disconnect_by_data (G_OBJECT (previous), dialog); + g_hash_table_remove (dialog->priv->bars, name); + } + + gvc_channel_bar_set_control (GVC_CHANNEL_BAR (dialog->priv->effects_bar), control); + + if (control != NULL) { + name = mate_mixer_stream_control_get_name (control); + g_hash_table_insert (dialog->priv->bars, + (gpointer) name, + dialog->priv->effects_bar); + + gtk_widget_set_sensitive (GTK_WIDGET (dialog->priv->effects_bar), TRUE); + } else + gtk_widget_set_sensitive (GTK_WIDGET (dialog->priv->effects_bar), FALSE); +} + +static void +on_stream_control_added (MateMixerStream *stream, + const gchar *name, + GvcMixerDialog *dialog) +{ + MateMixerStreamControl *control; + MateMixerStreamControlRole role; + + control = mate_mixer_stream_get_control (stream, name); + if G_UNLIKELY (control == NULL) + return; + + role = mate_mixer_stream_control_get_role (control); + + if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) + add_application_control (dialog, control); +} + +static void +on_stream_control_removed (MateMixerStream *stream, + const gchar *name, + GvcMixerDialog *dialog) +{ + MateMixerStreamControl *control; + + control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); + if (control != NULL) { + const gchar *input_name = mate_mixer_stream_control_get_name (control); + + if (strcmp (name, input_name) == 0) { + // XXX probably can't even happen, but handle it somehow + return; + } + } + + control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->output_bar)); + if (control != NULL) { + const gchar *input_name = mate_mixer_stream_control_get_name (control); + + if (strcmp (name, input_name) == 0) { + // XXX probably can't even happen, but handle it somehow + return; + } + } + + /* No way to be sure that it is an application control, but we don't have + * any other than application bars that could match the name */ + remove_application_control (dialog, name); +} + +static void +add_stream (GvcMixerDialog *dialog, MateMixerStream *stream) +{ + GtkTreeModel *model = NULL; + GtkTreeIter iter; + const gchar *speakers = NULL; + const GList *controls; + gboolean is_default = FALSE; + MateMixerDirection direction; + + direction = mate_mixer_stream_get_direction (stream); + + if (direction == MATE_MIXER_DIRECTION_INPUT) { + MateMixerStream *input; + + input = mate_mixer_context_get_default_input_stream (dialog->priv->context); + if (stream == input) { + bar_set_stream (dialog, dialog->priv->input_bar, stream); + + update_input_settings (dialog); + is_default = TRUE; + } + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); + } + else if (direction == MATE_MIXER_DIRECTION_OUTPUT) { + MateMixerStream *output; + MateMixerStreamControl *control; + + output = mate_mixer_context_get_default_output_stream (dialog->priv->context); + if (stream == output) { + bar_set_stream (dialog, dialog->priv->output_bar, stream); + + update_output_settings (dialog); + is_default = TRUE; + } + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); + + control = mate_mixer_stream_get_default_control (stream); + if (G_LIKELY (control != NULL)) + speakers = gvc_channel_map_to_pretty_string (control); + } + + controls = mate_mixer_stream_list_controls (stream); + while (controls != NULL) { + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (controls->data); + MateMixerStreamControlRole role; + + role = mate_mixer_stream_control_get_role (control); + + if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) + add_application_control (dialog, control); + + controls = controls->next; + } + + if (model != NULL) { + const gchar *name; + const gchar *label; + + name = mate_mixer_stream_get_name (stream); + label = mate_mixer_stream_get_label (stream); + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + NAME_COLUMN, name, + LABEL_COLUMN, label, + ACTIVE_COLUMN, is_default, + SPEAKERS_COLUMN, speakers, + -1); + } + + // XXX find a way to disconnect when removed + g_signal_connect (G_OBJECT (stream), + "control-added", + G_CALLBACK (on_stream_control_added), + dialog); + g_signal_connect (G_OBJECT (stream), + "control-removed", + G_CALLBACK (on_stream_control_removed), + dialog); +} + +static void +update_device_test_visibility (GvcMixerDialog *dialog) +{ + MateMixerDevice *device; + MateMixerStream *stream; + + device = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), "device"); + if (G_UNLIKELY (device == NULL)) { + return; + } + + stream = find_device_test_stream (dialog, device); + + g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), + "show-button", (stream != NULL), + NULL); +} + +static void +on_context_stream_added (MateMixerContext *context, + const gchar *name, + GvcMixerDialog *dialog) +{ + MateMixerStream *stream; + MateMixerDirection direction; + GtkWidget *bar; + + stream = mate_mixer_context_get_stream (context, name); + if (G_UNLIKELY (stream == NULL)) + return; + + direction = mate_mixer_stream_get_direction (stream); + + /* If the newly added stream belongs to the currently selected device and + * the test button is hidden, this stream may be the one to allow the + * sound test and therefore we may need to enable the button */ + if (dialog->priv->hw_profile_combo != NULL && direction == MATE_MIXER_DIRECTION_OUTPUT) { + MateMixerDevice *device1; + MateMixerDevice *device2; + + device1 = mate_mixer_stream_get_device (stream); + device2 = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), + "device"); + + if (device1 == device2) { + gboolean show_button; + + g_object_get (G_OBJECT (dialog->priv->hw_profile_combo), + "show-button", &show_button, + NULL); + + if (show_button == FALSE) + update_device_test_visibility (dialog); + } + } + + bar = g_hash_table_lookup (dialog->priv->bars, name); + if (G_UNLIKELY (bar != NULL)) + return; + + add_stream (dialog, stream); +} + +static void +remove_stream (GvcMixerDialog *dialog, const gchar *name) +{ + GtkWidget *bar; + GtkTreeIter iter; + GtkTreeModel *model; + + bar = g_hash_table_lookup (dialog->priv->bars, name); + + if (bar != NULL) { + g_debug ("Removing stream %s from bar %s", + name, + gvc_channel_bar_get_name (GVC_CHANNEL_BAR (bar))); + + bar_set_stream (dialog, bar, NULL); + } + + /* Remove from any models */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + NAME_COLUMN, + &iter) == TRUE) + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + NAME_COLUMN, + &iter) == TRUE) + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); +} + +static void +remove_application_control (GvcMixerDialog *dialog, const gchar *name) +{ + GtkWidget *bar; + + bar = g_hash_table_lookup (dialog->priv->bars, name); + if (G_UNLIKELY (bar == NULL)) + return; + + g_debug ("Removing application stream %s", name); + + /* We could call bar_set_stream_control here, but that would pointlessly + * invalidate the channel bar, so just remove it ourselves */ + g_hash_table_remove (dialog->priv->bars, name); + + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (bar)), bar); + + if (G_UNLIKELY (dialog->priv->num_apps <= 0)) { + g_warn_if_reached (); + dialog->priv->num_apps = 1; + } + + dialog->priv->num_apps--; + + if (dialog->priv->num_apps == 0) + gtk_widget_show (dialog->priv->no_apps_label); +} + +static void +on_context_stream_removed (MateMixerContext *context, + const gchar *name, + GvcMixerDialog *dialog) +{ + if (dialog->priv->hw_profile_combo != NULL) { + gboolean show_button; + + g_object_get (G_OBJECT (dialog->priv->hw_profile_combo), + "show-button", &show_button, + NULL); + + if (show_button == TRUE) + update_device_test_visibility (dialog); + } + + remove_stream (dialog, name); +} + +static void +on_context_stored_control_added (MateMixerContext *context, + const gchar *name, + GvcMixerDialog *dialog) +{ + MateMixerStreamControl *control; + MateMixerStreamControlMediaRole media_role; + + control = MATE_MIXER_STREAM_CONTROL (mate_mixer_context_get_stored_control (context, name)); + if (G_UNLIKELY (control == NULL)) + return; + + media_role = mate_mixer_stream_control_get_media_role (control); + + if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT) + add_effects_control (dialog, control); +} + +static void +on_context_stored_control_removed (MateMixerContext *context, + const gchar *name, + GvcMixerDialog *dialog) +{ + GtkWidget *bar; + + bar = g_hash_table_lookup (dialog->priv->bars, name); + + if (bar != NULL) { + /* We only use a stored control in the effects bar */ + if (G_UNLIKELY (bar != dialog->priv->effects_bar)) { + g_warn_if_reached (); + return; + } + + bar_set_stream (dialog, bar, NULL); + } +} + +static gchar * +device_status (MateMixerDevice *device) +{ + guint inputs = 0; + guint outputs = 0; + gchar *inputs_str = NULL; + gchar *outputs_str = NULL; + const GList *streams; + + /* Get number of input and output streams in the device */ + streams = mate_mixer_device_list_streams (device); + while (streams != NULL) { + MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); + MateMixerDirection direction; + + direction = mate_mixer_stream_get_direction (stream); + + if (direction == MATE_MIXER_DIRECTION_INPUT) + inputs++; + else if (direction == MATE_MIXER_DIRECTION_OUTPUT) + outputs++; + + streams = streams->next; + } + + if (inputs == 0 && outputs == 0) { + /* translators: + * The device has been disabled */ + return g_strdup (_("Disabled")); + } + + if (outputs > 0) { + /* translators: + * The number of sound outputs on a particular device */ + outputs_str = g_strdup_printf (ngettext ("%u Output", + "%u Outputs", + outputs), + outputs); + } + + if (inputs > 0) { + /* translators: + * The number of sound inputs on a particular device */ + inputs_str = g_strdup_printf (ngettext ("%u Input", + "%u Inputs", + inputs), + inputs); + } + + if (inputs_str != NULL && outputs_str != NULL) { + gchar *ret = g_strdup_printf ("%s / %s", + outputs_str, + inputs_str); + g_free (outputs_str); + g_free (inputs_str); + return ret; + } + + if (inputs_str != NULL) + return inputs_str; + + return outputs_str; +} + +static void +update_device_info (GvcMixerDialog *dialog, MateMixerDevice *device) +{ + GtkTreeModel *model = NULL; + GtkTreeIter iter; + const gchar *label; + const gchar *profile_label = NULL; + gchar *status; + MateMixerSwitch *profile_switch; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); + + if (find_tree_item_by_name (model, + mate_mixer_device_get_name (device), + HW_NAME_COLUMN, + &iter) == FALSE) + return; + + label = mate_mixer_device_get_label (device); + + profile_switch = find_device_profile_switch (device); + if (profile_switch != NULL) { + MateMixerSwitchOption *active; + + active = mate_mixer_switch_get_active_option (profile_switch); + if (G_LIKELY (active != NULL)) + profile_label = mate_mixer_switch_option_get_label (active); + } + + status = device_status (device); + + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + HW_LABEL_COLUMN, label, + HW_PROFILE_COLUMN, profile_label, + HW_STATUS_COLUMN, status, + -1); + g_free (status); +} + +static void +on_device_profile_notify (MateMixerSwitch *swtch, + GParamSpec *pspec, + GvcMixerDialog *dialog) +{ + MateMixerSwitchOption *active; + + g_signal_handlers_block_by_func (G_OBJECT (dialog->priv->hw_profile_combo), + G_CALLBACK (on_combobox_option_changed), + dialog); + + active = mate_mixer_switch_get_active_option (swtch); + if (G_LIKELY (active != NULL)) { + const gchar *name = mate_mixer_switch_option_get_name (active); + + gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), + name); + } + + g_signal_handlers_unblock_by_func (G_OBJECT (dialog->priv->hw_profile_combo), + G_CALLBACK (on_combobox_option_changed), + dialog); + + // XXX find device + // update_device_info (dialog, device); +} + +static void +add_device (GvcMixerDialog *dialog, MateMixerDevice *device) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GIcon *icon; + const gchar *name; + const gchar *label; + gchar *status; + const gchar *profile_label = NULL; + MateMixerSwitch *profile_switch; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); + + name = mate_mixer_device_get_name (device); + label = mate_mixer_device_get_label (device); + + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + HW_NAME_COLUMN, + &iter) == FALSE) + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + + icon = g_themed_icon_new_with_default_fallbacks (mate_mixer_device_get_icon (device)); + + profile_switch = find_device_profile_switch (device); + if (profile_switch != NULL) { + MateMixerSwitchOption *active; + + active = mate_mixer_switch_get_active_option (profile_switch); + if (G_LIKELY (active != NULL)) + profile_label = mate_mixer_switch_option_get_label (active); + + g_signal_connect (G_OBJECT (device), + "notify::active-option", + G_CALLBACK (on_device_profile_notify), + dialog); + } + + status = device_status (device); + + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + HW_NAME_COLUMN, name, + HW_LABEL_COLUMN, label, + HW_ICON_COLUMN, icon, + HW_PROFILE_COLUMN, profile_label, + HW_STATUS_COLUMN, status, + -1); + g_free (status); + +} + +static void +on_context_device_added (MateMixerContext *context, const gchar *name, GvcMixerDialog *dialog) +{ + MateMixerDevice *device; + + device = mate_mixer_context_get_device (context, name); + if (G_UNLIKELY (device == NULL)) + return; + + add_device (dialog, device); +} + +static void +on_context_device_removed (MateMixerContext *context, + const gchar *name, + GvcMixerDialog *dialog) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + /* Remove from the device model */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); + + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + HW_NAME_COLUMN, + &iter) == TRUE) + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); +} + +static void +make_label_bold (GtkLabel *label) +{ + PangoFontDescription *font_desc; + + font_desc = pango_font_description_new (); + + pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); + + /* This will only affect the weight of the font, the rest is + * from the current state of the widget, which comes from the + * theme or user prefs, since the font desc only has the + * weight flag turned on. */ +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_override_font (GTK_WIDGET (label), font_desc); +#else + gtk_widget_modify_font (GTK_WIDGET (label), font_desc); +#endif + pango_font_description_free (font_desc); +} + +static void +on_input_radio_toggled (GtkCellRendererToggle *renderer, + gchar *path_str, + GvcMixerDialog *dialog) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + gboolean toggled = FALSE; + gchar *name = NULL; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); + path = gtk_tree_path_new_from_string (path_str); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + + gtk_tree_model_get (model, &iter, + NAME_COLUMN, &name, + ACTIVE_COLUMN, &toggled, + -1); + if (toggled ^ 1) { + MateMixerStream *stream; + MateMixerBackendFlags flags; + + stream = mate_mixer_context_get_stream (dialog->priv->context, name); + if (G_UNLIKELY (stream == NULL)) { + g_warn_if_reached (); + g_free (name); + return; + } + + g_debug ("Default input stream selection changed to %s", name); + + // XXX cache this + flags = mate_mixer_context_get_backend_flags (dialog->priv->context); + + if (flags & MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM) + mate_mixer_context_set_default_input_stream (dialog->priv->context, stream); + else + set_input_stream (dialog, stream); + } + g_free (name); +} + +static void +on_output_radio_toggled (GtkCellRendererToggle *renderer, + gchar *path_str, + GvcMixerDialog *dialog) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + gboolean toggled = FALSE; + gchar *name = NULL; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); + path = gtk_tree_path_new_from_string (path_str); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + + gtk_tree_model_get (model, &iter, + NAME_COLUMN, &name, + ACTIVE_COLUMN, &toggled, + -1); + if (toggled ^ 1) { + MateMixerStream *stream; + MateMixerBackendFlags flags; + + stream = mate_mixer_context_get_stream (dialog->priv->context, name); + if (G_UNLIKELY (stream == NULL)) { + g_warn_if_reached (); + g_free (name); + return; + } + + g_debug ("Default output stream selection changed to %s", name); + + // XXX cache this + flags = mate_mixer_context_get_backend_flags (dialog->priv->context); + + if (flags & MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM) + mate_mixer_context_set_default_output_stream (dialog->priv->context, stream); + else + set_output_stream (dialog, stream); + } + g_free (name); +} + +static void +stream_name_to_text (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + gchar *label; + gchar *speakers; + + gtk_tree_model_get (model, iter, + LABEL_COLUMN, &label, + SPEAKERS_COLUMN, &speakers, + -1); + + if (speakers != NULL) { + gchar *str = g_strdup_printf ("%s\n%s", + label, + speakers); + + g_object_set (cell, "markup", str, NULL); + g_free (str); + g_free (speakers); + } else { + g_object_set (cell, "text", label, NULL); + } + + g_free (label); +} + +static gint +compare_stream_treeview_items (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *desc_a = NULL; + gchar *desc_b = NULL; + gint result; + + gtk_tree_model_get (model, a, + LABEL_COLUMN, &desc_a, + -1); + gtk_tree_model_get (model, b, + LABEL_COLUMN, &desc_b, + -1); + + if (desc_a == NULL) { + g_free (desc_b); + return -1; + } + if (desc_b == NULL) { + g_free (desc_a); + return 1; + } + + result = g_ascii_strcasecmp (desc_a, desc_b); + + g_free (desc_a); + g_free (desc_b); + return result; +} + +static GtkWidget * +create_stream_treeview (GvcMixerDialog *dialog, GCallback on_toggled) +{ + GtkWidget *treeview; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + treeview = gtk_tree_view_new (); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); + + store = gtk_list_store_new (NUM_COLUMNS, + G_TYPE_ICON, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + G_TYPE_STRING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), + GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_toggle_new (); + gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE); + + column = gtk_tree_view_column_new_with_attributes (NULL, + renderer, + "active", ACTIVE_COLUMN, + NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + g_signal_connect (G_OBJECT (renderer), + "toggled", + G_CALLBACK (on_toggled), + dialog); + + gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1, + _("Name"), + gtk_cell_renderer_text_new (), + stream_name_to_text, + NULL, NULL); + + /* Keep the list of streams sorted by the name */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + LABEL_COLUMN, + GTK_SORT_ASCENDING); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + LABEL_COLUMN, + compare_stream_treeview_items, + NULL, NULL); + return treeview; +} + +static void +on_test_speakers_clicked (GvcComboBox *widget, GvcMixerDialog *dialog) +{ + GtkWidget *d, + *test, + *container; + gchar *title; + MateMixerDevice *device; + MateMixerStream *stream; + + device = g_object_get_data (G_OBJECT (widget), "device"); + if (G_UNLIKELY (device == NULL)) { + g_warn_if_reached (); + return; + } + + stream = find_device_test_stream (dialog, device); + if (G_UNLIKELY (stream == NULL)) { + g_warn_if_reached (); + return; + } + + title = g_strdup_printf (_("Speaker Testing for %s"), + mate_mixer_device_get_label (device)); + + d = gtk_dialog_new_with_buttons (title, + GTK_WINDOW (dialog), + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + "gtk-close", + GTK_RESPONSE_CLOSE, + NULL); + g_free (title); + + gtk_window_set_resizable (GTK_WINDOW (d), FALSE); + + test = gvc_speaker_test_new (stream); + gtk_widget_show (test); + + container = gtk_dialog_get_content_area (GTK_DIALOG (d)); + gtk_container_add (GTK_CONTAINER (container), test); + + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); +} + +static GtkWidget * +create_profile_combo_box (GvcMixerDialog *dialog, + MateMixerSwitch *swtch, + const gchar *name, + const GList *items, + const gchar *active) +{ + GtkWidget *combobox; + + combobox = gvc_combo_box_new (name); + + gvc_combo_box_set_options (GVC_COMBO_BOX (combobox), items); + gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), active); + + gvc_combo_box_set_size_group (GVC_COMBO_BOX (combobox), + dialog->priv->size_group, + FALSE); + + g_object_set (G_OBJECT (combobox), + "button-label", _("Test Speakers"), + NULL); + + g_object_set_data_full (G_OBJECT (combobox), + "switch", + g_object_ref (swtch), + g_object_unref); + + g_signal_connect (G_OBJECT (combobox), + "changed", + G_CALLBACK (on_combobox_option_changed), + dialog); + + g_signal_connect (G_OBJECT (combobox), + "button-clicked", + G_CALLBACK (on_test_speakers_clicked), + dialog); + + return combobox; +} + +static void +on_device_selection_changed (GtkTreeSelection *selection, GvcMixerDialog *dialog) +{ + GtkTreeIter iter; + gchar *name; + MateMixerDevice *device; + MateMixerSwitch *profile_switch; + + g_debug ("Device selection changed"); + + if (dialog->priv->hw_profile_combo != NULL) { + gtk_container_remove (GTK_CONTAINER (dialog->priv->hw_settings_box), + dialog->priv->hw_profile_combo); + + dialog->priv->hw_profile_combo = NULL; + } + + if (gtk_tree_selection_get_selected (selection, NULL, &iter) == FALSE) + return; + + gtk_tree_model_get (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)), + &iter, + HW_NAME_COLUMN, &name, + -1); + + device = mate_mixer_context_get_device (dialog->priv->context, name); + if (G_UNLIKELY (device == NULL)) { + g_warn_if_reached (); + g_free (name); + return; + } + g_free (name); + + /* Profile/speaker test combo */ + profile_switch = find_device_profile_switch (device); + if (profile_switch != NULL) { + const GList *options; + const gchar *active_name = NULL; + MateMixerSwitchOption *active; + + options = mate_mixer_switch_list_options (profile_switch); + active = mate_mixer_switch_get_active_option (profile_switch); + if (active != NULL) + active_name = mate_mixer_switch_option_get_name (active); + + dialog->priv->hw_profile_combo = + create_profile_combo_box (dialog, + profile_switch, + _("_Profile:"), + options, + active_name); + + g_object_set_data_full (G_OBJECT (dialog->priv->hw_profile_combo), + "device", + g_object_ref (device), + g_object_unref); + + gtk_box_pack_start (GTK_BOX (dialog->priv->hw_settings_box), + dialog->priv->hw_profile_combo, + TRUE, TRUE, 6); + + /* Enable the speaker test button if the selected device + * is capable of sound output */ + update_device_test_visibility (dialog); + + gtk_widget_show (dialog->priv->hw_profile_combo); + } +} + +static void +on_notebook_switch_page (GtkNotebook *notebook, + GtkWidget *page, + guint page_num, + GvcMixerDialog *dialog) +{ + MateMixerStreamControl *control; + + // XXX because this is called too early in constructor + if (G_UNLIKELY (dialog->priv->input_bar == NULL)) + return; + + control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); + if (control == NULL) + return; + + if (page_num == PAGE_INPUT) + mate_mixer_stream_control_set_monitor_enabled (control, TRUE); + else + mate_mixer_stream_control_set_monitor_enabled (control, FALSE); +} + +static void +device_name_to_text (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + gchar *label = NULL; + gchar *profile = NULL; + gchar *status = NULL; + + gtk_tree_model_get (model, iter, + HW_LABEL_COLUMN, &label, + HW_PROFILE_COLUMN, &profile, + HW_STATUS_COLUMN, &status, + -1); + + if (G_LIKELY (status != NULL)) { + gchar *str; + + if (profile != NULL) + str = g_strdup_printf ("%s\n%s\n%s", + label, + status, + profile); + else + str = g_strdup_printf ("%s\n%s", + label, + status); + + g_object_set (cell, "markup", str, NULL); + g_free (str); + g_free (profile); + g_free (status); + } else + g_object_set (cell, "text", label, NULL); + + g_free (label); +} + +static gint +compare_device_treeview_items (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *desc_a = NULL; + gchar *desc_b = NULL; + gint result; + + gtk_tree_model_get (model, a, + HW_LABEL_COLUMN, &desc_a, + -1); + gtk_tree_model_get (model, b, + HW_LABEL_COLUMN, &desc_b, + -1); + + result = g_ascii_strcasecmp (desc_a, desc_b); + + g_free (desc_a); + g_free (desc_b); + return result; +} + +static GtkWidget * +create_device_treeview (GvcMixerDialog *dialog, GCallback on_changed) +{ + GtkWidget *treeview; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + treeview = gtk_tree_view_new (); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + g_signal_connect (G_OBJECT (selection), + "changed", + on_changed, + dialog); + + store = gtk_list_store_new (HW_NUM_COLUMNS, + G_TYPE_ICON, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), + GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set (G_OBJECT (renderer), + "stock-size", + GTK_ICON_SIZE_DIALOG, + NULL); + + column = gtk_tree_view_column_new_with_attributes (NULL, + renderer, + "gicon", HW_ICON_COLUMN, + NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1, + _("Name"), + gtk_cell_renderer_text_new (), + device_name_to_text, + NULL, NULL); + + /* Keep the list of streams sorted by the name */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + HW_LABEL_COLUMN, + GTK_SORT_ASCENDING); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + HW_LABEL_COLUMN, + compare_device_treeview_items, + NULL, NULL); + return treeview; +} + +static void +dialog_accel_cb (GtkAccelGroup *accelgroup, + GObject *object, + guint key, + GdkModifierType mod, + GvcMixerDialog *self) +{ + gint num = -1; + gint i; + + for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { + if (tab_accel_keys[i] == key) { + num = i; + break; + } + } + + if (num != -1) + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num); +} + +static GObject * +gvc_mixer_dialog_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + GvcMixerDialog *self; + GtkWidget *main_vbox; + GtkWidget *label; + GtkWidget *alignment; + GtkWidget *box; + GtkWidget *sbox; + GtkWidget *ebox; + GtkTreeSelection *selection; + GtkAccelGroup *accel_group; + GClosure *closure; + GtkTreeIter iter; + gint i; + const GList *list; + + object = G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->constructor (type, + n_construct_properties, + construct_params); + + self = GVC_MIXER_DIALOG (object); + + gtk_dialog_add_button (GTK_DIALOG (self), "gtk-close", GTK_RESPONSE_OK); + + main_vbox = gtk_dialog_get_content_area (GTK_DIALOG (self)); + gtk_box_set_spacing (GTK_BOX (main_vbox), 2); + + gtk_container_set_border_width (GTK_CONTAINER (self), 6); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->output_stream_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); +#else + self->priv->output_stream_box = gtk_hbox_new (FALSE, 12); +#endif + + alignment = gtk_alignment_new (0, 0, 1, 1); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0); + gtk_container_add (GTK_CONTAINER (alignment), self->priv->output_stream_box); + gtk_box_pack_start (GTK_BOX (main_vbox), + alignment, + FALSE, FALSE, 0); + + self->priv->output_bar = create_bar (self, TRUE, TRUE); + gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->output_bar), + _("_Output volume: ")); + + gtk_widget_show (self->priv->output_bar); + gtk_widget_set_sensitive (self->priv->output_bar, FALSE); + + gtk_box_pack_start (GTK_BOX (self->priv->output_stream_box), + self->priv->output_bar, TRUE, TRUE, 12); + + self->priv->notebook = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (main_vbox), + self->priv->notebook, + TRUE, TRUE, 0); + + g_signal_connect (G_OBJECT (self->priv->notebook), + "switch-page", + G_CALLBACK (on_notebook_switch_page), + self); + + gtk_container_set_border_width (GTK_CONTAINER (self->priv->notebook), 5); + + /* Set up accels (borrowed from Empathy) */ + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group (GTK_WINDOW (self), accel_group); + + for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { + closure = g_cclosure_new (G_CALLBACK (dialog_accel_cb), + self, + NULL); + gtk_accel_group_connect (accel_group, + tab_accel_keys[i], + GDK_MOD1_MASK, + 0, + closure); + } + + g_object_unref (accel_group); + + /* Create notebook pages */ +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->sound_effects_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); +#else + self->priv->sound_effects_box = gtk_vbox_new (FALSE, 6); +#endif + gtk_container_set_border_width (GTK_CONTAINER (self->priv->sound_effects_box), 12); + + label = gtk_label_new (_("Sound Effects")); + gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), + self->priv->sound_effects_box, + label); + + self->priv->effects_bar = create_bar (self, TRUE, TRUE); + gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box), + self->priv->effects_bar, FALSE, FALSE, 0); + + gvc_channel_bar_set_show_marks (GVC_CHANNEL_BAR (self->priv->effects_bar), FALSE); + gvc_channel_bar_set_extended (GVC_CHANNEL_BAR (self->priv->effects_bar), FALSE); + + gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->effects_bar), + _("_Alert volume: ")); + + gtk_widget_show (self->priv->effects_bar); + gtk_widget_set_sensitive (self->priv->effects_bar, FALSE); + + self->priv->sound_theme_chooser = gvc_sound_theme_chooser_new (); + + gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box), + self->priv->sound_theme_chooser, + TRUE, TRUE, 6); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->hw_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else + self->priv->hw_box = gtk_vbox_new (FALSE, 12); +#endif + gtk_container_set_border_width (GTK_CONTAINER (self->priv->hw_box), 12); + + label = gtk_label_new (_("Hardware")); + gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), + self->priv->hw_box, + label); + + box = gtk_frame_new (_("C_hoose a device to configure:")); + label = gtk_frame_get_label_widget (GTK_FRAME (box)); + make_label_bold (GTK_LABEL (label)); + gtk_label_set_use_underline (GTK_LABEL (label), TRUE); + gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, TRUE, TRUE, 0); + + alignment = gtk_alignment_new (0, 0, 1, 1); + gtk_container_add (GTK_CONTAINER (box), alignment); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); + + self->priv->hw_treeview = create_device_treeview (self, + G_CALLBACK (on_device_selection_changed)); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->hw_treeview); + + box = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box), + GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (box), self->priv->hw_treeview); + gtk_container_add (GTK_CONTAINER (alignment), box); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + box = gtk_frame_new (_("Settings for the selected device:")); + label = gtk_frame_get_label_widget (GTK_FRAME (box)); + make_label_bold (GTK_LABEL (label)); + gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, FALSE, TRUE, 12); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->hw_settings_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else + self->priv->hw_settings_box = gtk_vbox_new (FALSE, 12); +#endif + + gtk_container_add (GTK_CONTAINER (box), self->priv->hw_settings_box); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->input_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else + self->priv->input_box = gtk_vbox_new (FALSE, 12); +#endif + + gtk_container_set_border_width (GTK_CONTAINER (self->priv->input_box), 12); + + label = gtk_label_new (_("Input")); + gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), + self->priv->input_box, + label); + + self->priv->input_bar = create_bar (self, TRUE, TRUE); + + gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->input_bar), + _("_Input volume: ")); + + gvc_channel_bar_set_low_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar), + "audio-input-microphone-low"); + gvc_channel_bar_set_high_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar), + "audio-input-microphone-high"); + + gtk_widget_show (self->priv->input_bar); + gtk_widget_set_sensitive (self->priv->input_bar, FALSE); + + alignment = gtk_alignment_new (0, 0, 1, 1); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); + gtk_container_add (GTK_CONTAINER (alignment), self->priv->input_bar); + gtk_box_pack_start (GTK_BOX (self->priv->input_box), + alignment, + FALSE, FALSE, 0); + +#if GTK_CHECK_VERSION (3, 0, 0) + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + sbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + ebox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); +#else + box = gtk_hbox_new (FALSE, 6); + sbox = gtk_hbox_new (FALSE, 6); + ebox = gtk_hbox_new (FALSE, 6); +#endif + + gtk_box_pack_start (GTK_BOX (self->priv->input_box), + box, + FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (box), + sbox, + FALSE, FALSE, 0); + + label = gtk_label_new (_("Input level:")); + gtk_box_pack_start (GTK_BOX (sbox), + label, + FALSE, FALSE, 0); + gtk_size_group_add_widget (self->priv->size_group, sbox); + + self->priv->input_level_bar = gvc_level_bar_new (); + gvc_level_bar_set_orientation (GVC_LEVEL_BAR (self->priv->input_level_bar), + GTK_ORIENTATION_HORIZONTAL); + gvc_level_bar_set_scale (GVC_LEVEL_BAR (self->priv->input_level_bar), + GVC_LEVEL_SCALE_LINEAR); + gtk_box_pack_start (GTK_BOX (box), + self->priv->input_level_bar, + TRUE, TRUE, 6); + + gtk_box_pack_start (GTK_BOX (box), + ebox, + FALSE, FALSE, 0); + gtk_size_group_add_widget (self->priv->size_group, ebox); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->input_settings_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); +#else + self->priv->input_settings_box = gtk_hbox_new (FALSE, 6); +#endif + gtk_box_pack_start (GTK_BOX (self->priv->input_box), + self->priv->input_settings_box, + FALSE, FALSE, 0); + + box = gtk_frame_new (_("C_hoose a device for sound input:")); + label = gtk_frame_get_label_widget (GTK_FRAME (box)); + make_label_bold (GTK_LABEL (label)); + gtk_label_set_use_underline (GTK_LABEL (label), TRUE); + gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (self->priv->input_box), box, TRUE, TRUE, 0); + + alignment = gtk_alignment_new (0, 0, 1, 1); + gtk_container_add (GTK_CONTAINER (box), alignment); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); + + self->priv->input_treeview = + create_stream_treeview (self, G_CALLBACK (on_input_radio_toggled)); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->input_treeview); + + box = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box), + GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (box), self->priv->input_treeview); + gtk_container_add (GTK_CONTAINER (alignment), box); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->input_treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + /* Output page */ + self->priv->output_box = gtk_vbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (self->priv->output_box), 12); + label = gtk_label_new (_("Output")); + gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), + self->priv->output_box, + label); + + box = gtk_frame_new (_("C_hoose a device for sound output:")); + label = gtk_frame_get_label_widget (GTK_FRAME (box)); + make_label_bold (GTK_LABEL (label)); + gtk_label_set_use_underline (GTK_LABEL (label), TRUE); + gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, TRUE, TRUE, 0); + + alignment = gtk_alignment_new (0, 0, 1, 1); + gtk_container_add (GTK_CONTAINER (box), alignment); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); + + self->priv->output_treeview = create_stream_treeview (self, + G_CALLBACK (on_output_radio_toggled)); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->output_treeview); + + box = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box), + GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (box), self->priv->output_treeview); + gtk_container_add (GTK_CONTAINER (alignment), box); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->output_treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + box = gtk_frame_new (_("Settings for the selected device:")); + label = gtk_frame_get_label_widget (GTK_FRAME (box)); + make_label_bold (GTK_LABEL (label)); + gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); + gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, FALSE, FALSE, 12); + self->priv->output_settings_box = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (box), self->priv->output_settings_box); + + self->priv->output_settings_frame = box; + + /* Applications */ + self->priv->applications_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->applications_window), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self->priv->applications_window), + GTK_SHADOW_IN); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->applications_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else + self->priv->applications_box = gtk_vbox_new (FALSE, 12); +#endif + gtk_container_set_border_width (GTK_CONTAINER (self->priv->applications_box), 12); + +#if GTK_CHECK_VERSION (3, 8, 0) + gtk_container_add (GTK_CONTAINER (self->priv->applications_window), + self->priv->applications_box); +#else + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (self->priv->applications_window), + self->priv->applications_box); +#endif + + label = gtk_label_new (_("Applications")); + gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), + self->priv->applications_window, + label); + + self->priv->no_apps_label = gtk_label_new (_("No application is currently playing or recording audio.")); + gtk_box_pack_start (GTK_BOX (self->priv->applications_box), + self->priv->no_apps_label, + TRUE, TRUE, 0); + + gtk_widget_show_all (main_vbox); + + list = mate_mixer_context_list_streams (self->priv->context); + while (list != NULL) { + add_stream (self, MATE_MIXER_STREAM (list->data)); + list = list->next; + } + + list = mate_mixer_context_list_devices (self->priv->context); + while (list != NULL) { + add_device (self, MATE_MIXER_DEVICE (list->data)); + list = list->next; + } + + /* Find an event role stream */ + list = mate_mixer_context_list_stored_controls (self->priv->context); + while (list != NULL) { + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data); + MateMixerStreamControlMediaRole media_role; + + media_role = mate_mixer_stream_control_get_media_role (control); + + if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT) { + add_effects_control (self, control); + break; + } + list = list->next; + } + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview)); + + /* Select the first device in the list */ + // XXX handle no devices + if (gtk_tree_selection_get_selected (selection, NULL, NULL) == FALSE) { + GtkTreeModel *model = + gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->hw_treeview)); + + if (gtk_tree_model_get_iter_first (model, &iter)) + gtk_tree_selection_select_iter (selection, &iter); + } + + return object; +} + +static MateMixerContext * +gvc_mixer_dialog_get_context (GvcMixerDialog *dialog) +{ + return dialog->priv->context; +} + +static void +gvc_mixer_dialog_set_context (GvcMixerDialog *dialog, MateMixerContext *context) +{ + dialog->priv->context = g_object_ref (context); + + g_signal_connect (G_OBJECT (dialog->priv->context), + "stream-added", + G_CALLBACK (on_context_stream_added), + dialog); + g_signal_connect (G_OBJECT (dialog->priv->context), + "stream-removed", + G_CALLBACK (on_context_stream_removed), + dialog); + + g_signal_connect (G_OBJECT (dialog->priv->context), + "device-added", + G_CALLBACK (on_context_device_added), + dialog); + g_signal_connect (G_OBJECT (dialog->priv->context), + "device-removed", + G_CALLBACK (on_context_device_removed), + dialog); + + g_signal_connect (G_OBJECT (dialog->priv->context), + "notify::default-input-stream", + G_CALLBACK (on_context_default_input_stream_notify), + dialog); + g_signal_connect (G_OBJECT (dialog->priv->context), + "notify::default-output-stream", + G_CALLBACK (on_context_default_output_stream_notify), + dialog); + + g_signal_connect (G_OBJECT (dialog->priv->context), + "stored-control-added", + G_CALLBACK (on_context_stored_control_added), + dialog); + g_signal_connect (G_OBJECT (dialog->priv->context), + "stored-control-removed", + G_CALLBACK (on_context_stored_control_removed), + dialog); + + g_object_notify (G_OBJECT (dialog), "context"); +} + +static void +gvc_mixer_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GvcMixerDialog *self = GVC_MIXER_DIALOG (object); + + switch (prop_id) { + case PROP_CONTEXT: + gvc_mixer_dialog_set_context (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_mixer_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GvcMixerDialog *self = GVC_MIXER_DIALOG (object); + + switch (prop_id) { + case PROP_CONTEXT: + g_value_set_object (value, gvc_mixer_dialog_get_context (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_mixer_dialog_dispose (GObject *object) +{ + GvcMixerDialog *dialog = GVC_MIXER_DIALOG (object); + + if (dialog->priv->context != NULL) { + g_signal_handlers_disconnect_by_data (G_OBJECT (dialog->priv->context), + dialog); + + g_clear_object (&dialog->priv->context); + } + + G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->dispose (object); +} + +static void +gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = gvc_mixer_dialog_constructor; + object_class->dispose = gvc_mixer_dialog_dispose; + object_class->finalize = gvc_mixer_dialog_finalize; + object_class->set_property = gvc_mixer_dialog_set_property; + object_class->get_property = gvc_mixer_dialog_get_property; + + g_object_class_install_property (object_class, + PROP_CONTEXT, + g_param_spec_object ("context", + "Context", + "MateMixer context", + MATE_MIXER_TYPE_CONTEXT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (klass, sizeof (GvcMixerDialogPrivate)); +} + +static void +gvc_mixer_dialog_init (GvcMixerDialog *dialog) +{ + dialog->priv = GVC_MIXER_DIALOG_GET_PRIVATE (dialog); + + dialog->priv->bars = g_hash_table_new (g_str_hash, g_str_equal); + dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); +} + +static void +gvc_mixer_dialog_finalize (GObject *object) +{ + GvcMixerDialog *dialog; + + dialog = GVC_MIXER_DIALOG (object); + + g_hash_table_destroy (dialog->priv->bars); + + G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->finalize (object); +} + +GvcMixerDialog * +gvc_mixer_dialog_new (MateMixerContext *context) +{ + return g_object_new (GVC_TYPE_MIXER_DIALOG, + "icon-name", "multimedia-volume-control", + "title", _("Sound Preferences"), + "context", context, + NULL); +} + +gboolean +gvc_mixer_dialog_set_page (GvcMixerDialog *self, const gchar *page) +{ + guint num = 0; + + g_return_val_if_fail (GVC_IS_MIXER_DIALOG (self), FALSE); + + if (page != NULL) { + if (g_str_equal (page, "effects")) + num = PAGE_EFFECTS; + else if (g_str_equal (page, "hardware")) + num = PAGE_HARDWARE; + else if (g_str_equal (page, "input")) + num = PAGE_INPUT; + else if (g_str_equal (page, "output")) + num = PAGE_OUTPUT; + else if (g_str_equal (page, "applications")) + num = PAGE_APPLICATIONS; + } + + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num); + + return TRUE; +} diff --git a/mate-volume-control/gvc-mixer-dialog.h b/mate-volume-control/gvc-mixer-dialog.h new file mode 100644 index 0000000..22e522d --- /dev/null +++ b/mate-volume-control/gvc-mixer-dialog.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_MIXER_DIALOG_H +#define __GVC_MIXER_DIALOG_H + +#include +#include + +#include + +G_BEGIN_DECLS + +#define GVC_DIALOG_DBUS_NAME "org.mate.VolumeControl" + +#define GVC_TYPE_MIXER_DIALOG (gvc_mixer_dialog_get_type ()) +#define GVC_MIXER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialog)) +#define GVC_MIXER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogClass)) +#define GVC_IS_MIXER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_DIALOG)) +#define GVC_IS_MIXER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_DIALOG)) +#define GVC_MIXER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogClass)) + +typedef struct _GvcMixerDialog GvcMixerDialog; +typedef struct _GvcMixerDialogClass GvcMixerDialogClass; +typedef struct _GvcMixerDialogPrivate GvcMixerDialogPrivate; + +struct _GvcMixerDialog +{ + GtkDialog parent; + GvcMixerDialogPrivate *priv; +}; + +struct _GvcMixerDialogClass +{ + GtkDialogClass parent_class; +}; + +GType gvc_mixer_dialog_get_type (void) G_GNUC_CONST; + +GvcMixerDialog * gvc_mixer_dialog_new (MateMixerContext *context); + +gboolean gvc_mixer_dialog_set_page (GvcMixerDialog *dialog, + const gchar *page); + +G_END_DECLS + +#endif /* __GVC_MIXER_DIALOG_H */ diff --git a/mate-volume-control/gvc-sound-theme-chooser.c b/mate-volume-control/gvc-sound-theme-chooser.c new file mode 100644 index 0000000..303c08b --- /dev/null +++ b/mate-volume-control/gvc-sound-theme-chooser.c @@ -0,0 +1,1147 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Bastien Nocera + * Copyright (C) 2008 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gvc-sound-theme-chooser.h" +#include "sound-theme-file-utils.h" + +#define GVC_SOUND_THEME_CHOOSER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooserPrivate)) + +struct GvcSoundThemeChooserPrivate +{ + GtkWidget *combo_box; + GtkWidget *treeview; + GtkWidget *theme_box; + GtkWidget *selection_box; + GtkWidget *click_feedback_button; + GSettings *sound_settings; +}; + +static void gvc_sound_theme_chooser_class_init (GvcSoundThemeChooserClass *klass); +static void gvc_sound_theme_chooser_init (GvcSoundThemeChooser *sound_theme_chooser); +static void gvc_sound_theme_chooser_dispose (GObject *object); + +#if GTK_CHECK_VERSION (3, 0, 0) +G_DEFINE_TYPE (GvcSoundThemeChooser, gvc_sound_theme_chooser, GTK_TYPE_BOX) +#else +G_DEFINE_TYPE (GvcSoundThemeChooser, gvc_sound_theme_chooser, GTK_TYPE_VBOX) +#endif + +#define KEY_SOUNDS_SCHEMA "org.mate.sound" +#define EVENT_SOUNDS_KEY "event-sounds" +#define INPUT_SOUNDS_KEY "input-feedback-sounds" +#define SOUND_THEME_KEY "theme-name" + +#define DEFAULT_ALERT_ID "__default" +#define CUSTOM_THEME_NAME "__custom" +#define NO_SOUNDS_THEME_NAME "__no_sounds" + +enum { + THEME_DISPLAY_COL, + THEME_IDENTIFIER_COL, + THEME_PARENT_ID_COL, + THEME_NUM_COLS +}; + +enum { + ALERT_DISPLAY_COL, + ALERT_IDENTIFIER_COL, + ALERT_SOUND_TYPE_COL, + ALERT_ACTIVE_COL, + ALERT_NUM_COLS +}; + +enum { + SOUND_TYPE_UNSET, + SOUND_TYPE_OFF, + SOUND_TYPE_DEFAULT_FROM_THEME, + SOUND_TYPE_BUILTIN, + SOUND_TYPE_CUSTOM +}; + +static void +on_combobox_changed (GtkComboBox *widget, + GvcSoundThemeChooser *chooser) +{ + GtkTreeIter iter; + GtkTreeModel *model; + char *theme_name; + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser->priv->combo_box), &iter) == FALSE) { + return; + } + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box)); + gtk_tree_model_get (model, &iter, THEME_IDENTIFIER_COL, &theme_name, -1); + + g_assert (theme_name != NULL); + + /* It is necessary to update the theme name before any other setting as + * the "changed" notification will reload the contents of the widget */ + g_settings_set_string (chooser->priv->sound_settings, SOUND_THEME_KEY, theme_name); + + /* special case for no sounds */ + if (strcmp (theme_name, NO_SOUNDS_THEME_NAME) == 0) { + g_settings_set_boolean (chooser->priv->sound_settings, EVENT_SOUNDS_KEY, FALSE); + return; + } else { + g_settings_set_boolean (chooser->priv->sound_settings, EVENT_SOUNDS_KEY, TRUE); + } + + g_free (theme_name); + + /* FIXME: reset alert model */ +} + +static char * +load_index_theme_name (const char *index, + char **parent) +{ + GKeyFile *file; + char *indexname = NULL; + gboolean hidden; + + file = g_key_file_new (); + if (g_key_file_load_from_file (file, index, G_KEY_FILE_KEEP_TRANSLATIONS, NULL) == FALSE) { + g_key_file_free (file); + return NULL; + } + /* Don't add hidden themes to the list */ + hidden = g_key_file_get_boolean (file, "Sound Theme", "Hidden", NULL); + if (!hidden) { + indexname = g_key_file_get_locale_string (file, + "Sound Theme", + "Name", + NULL, + NULL); + + /* Save the parent theme, if there's one */ + if (parent != NULL) { + *parent = g_key_file_get_string (file, + "Sound Theme", + "Inherits", + NULL); + } + } + + g_key_file_free (file); + return indexname; +} + +static void +sound_theme_in_dir (GHashTable *hash, + const char *dir) +{ + GDir *d; + const char *name; + + d = g_dir_open (dir, 0, NULL); + if (d == NULL) { + return; + } + + while ((name = g_dir_read_name (d)) != NULL) { + char *dirname, *index, *indexname; + + /* Look for directories */ + dirname = g_build_filename (dir, name, NULL); + if (g_file_test (dirname, G_FILE_TEST_IS_DIR) == FALSE) { + g_free (dirname); + continue; + } + + /* Look for index files */ + index = g_build_filename (dirname, "index.theme", NULL); + g_free (dirname); + + /* Check the name of the theme in the index.theme file */ + indexname = load_index_theme_name (index, NULL); + g_free (index); + if (indexname == NULL) { + continue; + } + + g_hash_table_insert (hash, g_strdup (name), indexname); + } + + g_dir_close (d); +} + +static void +add_theme_to_store (const char *key, + const char *value, + GtkListStore *store) +{ + char *parent; + + parent = NULL; + + /* Get the parent, if we're checking the custom theme */ + if (strcmp (key, CUSTOM_THEME_NAME) == 0) { + char *name, *path; + + path = custom_theme_dir_path ("index.theme"); + name = load_index_theme_name (path, &parent); + g_free (name); + g_free (path); + } + gtk_list_store_insert_with_values (store, NULL, G_MAXINT, + THEME_DISPLAY_COL, value, + THEME_IDENTIFIER_COL, key, + THEME_PARENT_ID_COL, parent, + -1); + g_free (parent); +} + +static void +set_combox_for_theme_name (GvcSoundThemeChooser *chooser, + const char *name) +{ + GtkTreeIter iter; + GtkTreeModel *model; + gboolean found; + + /* If the name is empty, use "freedesktop" */ + if (name == NULL || *name == '\0') { + name = "freedesktop"; + } + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box)); + + if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) { + return; + } + + do { + char *value; + + gtk_tree_model_get (model, &iter, THEME_IDENTIFIER_COL, &value, -1); + found = (value != NULL && strcmp (value, name) == 0); + g_free (value); + + } while (!found && gtk_tree_model_iter_next (model, &iter)); + + /* When we can't find the theme we need to set, try to set the default + * one "freedesktop" */ + if (found) { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (chooser->priv->combo_box), &iter); + } else if (strcmp (name, "freedesktop") != 0) { + g_debug ("not found, falling back to fdo"); + set_combox_for_theme_name (chooser, "freedesktop"); + } +} + +static void +set_input_feedback_enabled (GvcSoundThemeChooser *chooser, + gboolean enabled) +{ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser->priv->click_feedback_button), + enabled); +} + +static void +setup_theme_selector (GvcSoundThemeChooser *chooser) +{ + GHashTable *hash; + GtkListStore *store; + GtkCellRenderer *renderer; + const char * const *data_dirs; + const char *data_dir; + char *dir; + guint i; + + /* Add the theme names and their display name to a hash table, + * makes it easy to avoid duplicate themes */ + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + data_dirs = g_get_system_data_dirs (); + for (i = 0; data_dirs[i] != NULL; i++) { + dir = g_build_filename (data_dirs[i], "sounds", NULL); + sound_theme_in_dir (hash, dir); + g_free (dir); + } + + data_dir = g_get_user_data_dir (); + dir = g_build_filename (data_dir, "sounds", NULL); + sound_theme_in_dir (hash, dir); + g_free (dir); + + /* If there isn't at least one theme, make everything + * insensitive, LAME! */ + if (g_hash_table_size (hash) == 0) { + gtk_widget_set_sensitive (GTK_WIDGET (chooser), FALSE); + g_warning ("Bad setup, install the freedesktop sound theme"); + g_hash_table_destroy (hash); + return; + } + + /* Setup the tree model, 3 columns: + * - internal theme name/directory + * - display theme name + * - the internal id for the parent theme, used for the custom theme */ + store = gtk_list_store_new (THEME_NUM_COLS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + + /* Add the themes to a combobox */ + gtk_list_store_insert_with_values (store, + NULL, + G_MAXINT, + THEME_DISPLAY_COL, _("No sounds"), + THEME_IDENTIFIER_COL, "__no_sounds", + THEME_PARENT_ID_COL, NULL, + -1); + g_hash_table_foreach (hash, (GHFunc) add_theme_to_store, store); + g_hash_table_destroy (hash); + + /* Set the display */ + gtk_combo_box_set_model (GTK_COMBO_BOX (chooser->priv->combo_box), + GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser->priv->combo_box), + renderer, + TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (chooser->priv->combo_box), + renderer, + "text", THEME_DISPLAY_COL, + NULL); + + g_signal_connect (G_OBJECT (chooser->priv->combo_box), + "changed", + G_CALLBACK (on_combobox_changed), + chooser); +} + +#define GVC_SOUND_SOUND (xmlChar *) "sound" +#define GVC_SOUND_NAME (xmlChar *) "name" +#define GVC_SOUND_FILENAME (xmlChar *) "filename" + +/* Adapted from yelp-toc-pager.c */ +static xmlChar * +xml_get_and_trim_names (xmlNodePtr node) +{ + xmlNodePtr cur, keep = NULL; + xmlChar *keep_lang = NULL; + xmlChar *value; + int j, keep_pri = INT_MAX; + + const gchar * const * langs = g_get_language_names (); + + value = NULL; + + for (cur = node->children; cur; cur = cur->next) { + if (! xmlStrcmp (cur->name, GVC_SOUND_NAME)) { + xmlChar *cur_lang = NULL; + int cur_pri = INT_MAX; + + cur_lang = xmlNodeGetLang (cur); + + if (cur_lang) { + for (j = 0; langs[j]; j++) { + if (g_str_equal (cur_lang, langs[j])) { + cur_pri = j; + break; + } + } + } else { + cur_pri = INT_MAX - 1; + } + + if (cur_pri <= keep_pri) { + if (keep_lang) + xmlFree (keep_lang); + if (value) + xmlFree (value); + + value = xmlNodeGetContent (cur); + + keep_lang = cur_lang; + keep_pri = cur_pri; + keep = cur; + } else { + if (cur_lang) + xmlFree (cur_lang); + } + } + } + + /* Delete all GVC_SOUND_NAME nodes */ + cur = node->children; + while (cur) { + xmlNodePtr this = cur; + cur = cur->next; + if (! xmlStrcmp (this->name, GVC_SOUND_NAME)) { + xmlUnlinkNode (this); + xmlFreeNode (this); + } + } + + return value; +} + +static void +populate_model_from_node (GvcSoundThemeChooser *chooser, + GtkTreeModel *model, + xmlNodePtr node) +{ + xmlNodePtr child; + xmlChar *filename; + xmlChar *name; + + filename = NULL; + name = xml_get_and_trim_names (node); + for (child = node->children; child; child = child->next) { + if (xmlNodeIsText (child)) { + continue; + } + + if (xmlStrcmp (child->name, GVC_SOUND_FILENAME) == 0) { + filename = xmlNodeGetContent (child); + } else if (xmlStrcmp (child->name, GVC_SOUND_NAME) == 0) { + /* EH? should have been trimmed */ + } + } + + if (filename != NULL && name != NULL) { + gtk_list_store_insert_with_values (GTK_LIST_STORE (model), + NULL, + G_MAXINT, + ALERT_IDENTIFIER_COL, filename, + ALERT_DISPLAY_COL, name, + ALERT_SOUND_TYPE_COL, _("Built-in"), + ALERT_ACTIVE_COL, FALSE, + -1); + } + + xmlFree (filename); + xmlFree (name); +} + +static void +populate_model_from_file (GvcSoundThemeChooser *chooser, + GtkTreeModel *model, + const char *filename) +{ + xmlDocPtr doc; + xmlNodePtr root; + xmlNodePtr child; + gboolean exists; + + exists = g_file_test (filename, G_FILE_TEST_EXISTS); + if (! exists) { + return; + } + + doc = xmlParseFile (filename); + if (doc == NULL) { + return; + } + + root = xmlDocGetRootElement (doc); + + for (child = root->children; child; child = child->next) { + if (xmlNodeIsText (child)) { + continue; + } + if (xmlStrcmp (child->name, GVC_SOUND_SOUND) != 0) { + continue; + } + + populate_model_from_node (chooser, model, child); + } + + xmlFreeDoc (doc); +} + +static void +populate_model_from_dir (GvcSoundThemeChooser *chooser, + GtkTreeModel *model, + const char *dirname) +{ + GDir *d; + const char *name; + + d = g_dir_open (dirname, 0, NULL); + if (d == NULL) { + return; + } + + while ((name = g_dir_read_name (d)) != NULL) { + char *path; + + if (! g_str_has_suffix (name, ".xml")) { + continue; + } + + path = g_build_filename (dirname, name, NULL); + populate_model_from_file (chooser, model, path); + g_free (path); + } +} + +static gboolean +save_alert_sounds (GvcSoundThemeChooser *chooser, + const char *id) +{ + const char *sounds[3] = { "bell-terminal", "bell-window-system", NULL }; + char *path; + + if (strcmp (id, DEFAULT_ALERT_ID) == 0) { + delete_old_files (sounds); + delete_disabled_files (sounds); + } else { + delete_old_files (sounds); + delete_disabled_files (sounds); + add_custom_file (sounds, id); + } + + /* And poke the directory so the theme gets updated */ + path = custom_theme_dir_path (NULL); + if (utime (path, NULL) != 0) { + g_warning ("Failed to update mtime for directory '%s': %s", + path, g_strerror (errno)); + } + g_free (path); + + return FALSE; +} + + +static void +update_alert_model (GvcSoundThemeChooser *chooser, + const char *id) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview)); + gtk_tree_model_get_iter_first (model, &iter); + do { + gboolean toggled; + char *this_id; + + gtk_tree_model_get (model, &iter, + ALERT_IDENTIFIER_COL, &this_id, + -1); + + if (strcmp (this_id, id) == 0) { + toggled = TRUE; + } else { + toggled = FALSE; + } + g_free (this_id); + + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + ALERT_ACTIVE_COL, toggled, + -1); + } while (gtk_tree_model_iter_next (model, &iter)); +} + +static void +update_alert (GvcSoundThemeChooser *chooser, + const char *alert_id) +{ + GtkTreeModel *theme_model; + GtkTreeIter iter; + char *theme; + char *parent; + gboolean is_custom; + gboolean is_default; + gboolean add_custom; + gboolean remove_custom; + + theme_model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box)); + /* Get the current theme's name, and set the parent */ + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser->priv->combo_box), &iter) == FALSE) { + return; + } + + gtk_tree_model_get (theme_model, &iter, + THEME_IDENTIFIER_COL, &theme, + THEME_IDENTIFIER_COL, &parent, + -1); + is_custom = strcmp (theme, CUSTOM_THEME_NAME) == 0; + is_default = strcmp (alert_id, DEFAULT_ALERT_ID) == 0; + + /* So a few possibilities: + * 1. Named theme, default alert selected: noop + * 2. Named theme, alternate alert selected: create new custom with sound + * 3. Custom theme, default alert selected: remove sound and possibly custom + * 4. Custom theme, alternate alert selected: update custom sound + */ + add_custom = FALSE; + remove_custom = FALSE; + if (! is_custom && is_default) { + /* remove custom just in case */ + remove_custom = TRUE; + } else if (! is_custom && ! is_default) { + create_custom_theme (parent); + save_alert_sounds (chooser, alert_id); + add_custom = TRUE; + } else if (is_custom && is_default) { + save_alert_sounds (chooser, alert_id); + /* after removing files check if it is empty */ + if (custom_theme_dir_is_empty ()) { + remove_custom = TRUE; + } + } else if (is_custom && ! is_default) { + save_alert_sounds (chooser, alert_id); + } + + if (add_custom) { + gtk_list_store_insert_with_values (GTK_LIST_STORE (theme_model), + NULL, + G_MAXINT, + THEME_DISPLAY_COL, _("Custom"), + THEME_IDENTIFIER_COL, CUSTOM_THEME_NAME, + THEME_PARENT_ID_COL, theme, + -1); + set_combox_for_theme_name (chooser, CUSTOM_THEME_NAME); + } else if (remove_custom) { + gtk_tree_model_get_iter_first (theme_model, &iter); + do { + char *this_parent; + + gtk_tree_model_get (theme_model, &iter, + THEME_PARENT_ID_COL, &this_parent, + -1); + if (this_parent != NULL && strcmp (this_parent, CUSTOM_THEME_NAME) != 0) { + g_free (this_parent); + gtk_list_store_remove (GTK_LIST_STORE (theme_model), &iter); + break; + } + g_free (this_parent); + } while (gtk_tree_model_iter_next (theme_model, &iter)); + + delete_custom_theme_dir (); + + set_combox_for_theme_name (chooser, parent); + } + + update_alert_model (chooser, alert_id); + + g_free (theme); + g_free (parent); +} + +static void +on_alert_toggled (GtkCellRendererToggle *renderer, + char *path_str, + GvcSoundThemeChooser *chooser) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + gboolean toggled; + char *id; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview)); + + path = gtk_tree_path_new_from_string (path_str); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + + id = NULL; + gtk_tree_model_get (model, &iter, + ALERT_IDENTIFIER_COL, &id, + ALERT_ACTIVE_COL, &toggled, + -1); + + toggled ^= 1; + if (toggled) { + update_alert (chooser, id); + } + + g_free (id); +} + +static void +play_preview_for_path (GvcSoundThemeChooser *chooser, GtkTreePath *path) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeIter theme_iter; + gchar *id = NULL; + gchar *parent_theme = NULL; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview)); + if (gtk_tree_model_get_iter (model, &iter, path) == FALSE) + return; + + gtk_tree_model_get (model, &iter, + ALERT_IDENTIFIER_COL, &id, + -1); + if (id == NULL) + return; + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser->priv->combo_box), &theme_iter)) { + GtkTreeModel *theme_model; + gchar *theme_id = NULL; + gchar *parent_id = NULL; + + theme_model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser->priv->combo_box)); + + gtk_tree_model_get (theme_model, &theme_iter, + THEME_IDENTIFIER_COL, &theme_id, + THEME_PARENT_ID_COL, &parent_id, -1); + if (theme_id && strcmp (theme_id, CUSTOM_THEME_NAME) == 0) + parent_theme = g_strdup (parent_id); + + g_free (theme_id); + g_free (parent_id); + } + + /* special case: for the default item on custom themes + * play the alert for the parent theme */ + if (strcmp (id, DEFAULT_ALERT_ID) == 0) { + if (parent_theme != NULL) { + ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0, + CA_PROP_APPLICATION_NAME, _("Sound Preferences"), + CA_PROP_EVENT_ID, "bell-window-system", + CA_PROP_CANBERRA_XDG_THEME_NAME, parent_theme, + CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"), + CA_PROP_CANBERRA_CACHE_CONTROL, "never", + CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", +#ifdef CA_PROP_CANBERRA_ENABLE + CA_PROP_CANBERRA_ENABLE, "1", +#endif + NULL); + } else { + ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0, + CA_PROP_APPLICATION_NAME, _("Sound Preferences"), + CA_PROP_EVENT_ID, "bell-window-system", + CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"), + CA_PROP_CANBERRA_CACHE_CONTROL, "never", + CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", +#ifdef CA_PROP_CANBERRA_ENABLE + CA_PROP_CANBERRA_ENABLE, "1", +#endif + NULL); + } + } else { + ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0, + CA_PROP_APPLICATION_NAME, _("Sound Preferences"), + CA_PROP_MEDIA_FILENAME, id, + CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"), + CA_PROP_CANBERRA_CACHE_CONTROL, "never", + CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", +#ifdef CA_PROP_CANBERRA_ENABLE + CA_PROP_CANBERRA_ENABLE, "1", +#endif + NULL); + + } + g_free (parent_theme); + g_free (id); +} + +static void +on_treeview_row_activated (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + GvcSoundThemeChooser *chooser) +{ + play_preview_for_path (chooser, path); +} + +static void +on_treeview_selection_changed (GtkTreeSelection *selection, + GvcSoundThemeChooser *chooser) +{ + GList *paths; + GtkTreeModel *model; + GtkTreePath *path; + + if (chooser->priv->treeview == NULL) + return; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview)); + + paths = gtk_tree_selection_get_selected_rows (selection, &model); + if (paths == NULL) + return; + + path = paths->data; + play_preview_for_path (chooser, path); + + g_list_foreach (paths, (GFunc)gtk_tree_path_free, NULL); + g_list_free (paths); +} + +static GtkWidget * +create_alert_treeview (GvcSoundThemeChooser *chooser) +{ + GtkListStore *store; + GtkWidget *treeview; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + treeview = gtk_tree_view_new (); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); + g_signal_connect (G_OBJECT (treeview), + "row-activated", + G_CALLBACK (on_treeview_row_activated), + chooser); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), + "changed", + G_CALLBACK (on_treeview_selection_changed), + chooser); + + /* Setup the tree model, 3 columns: + * - display name + * - sound id + * - sound type + */ + store = gtk_list_store_new (ALERT_NUM_COLS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN); + + gtk_list_store_insert_with_values (store, + NULL, + G_MAXINT, + ALERT_IDENTIFIER_COL, DEFAULT_ALERT_ID, + ALERT_DISPLAY_COL, _("Default"), + ALERT_SOUND_TYPE_COL, _("From theme"), + ALERT_ACTIVE_COL, TRUE, + -1); + + populate_model_from_dir (chooser, GTK_TREE_MODEL (store), SOUND_SET_DIR); + + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), + GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_toggle_new (); + gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE); + + column = gtk_tree_view_column_new_with_attributes (NULL, + renderer, + "active", ALERT_ACTIVE_COL, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + g_signal_connect (renderer, + "toggled", + G_CALLBACK (on_alert_toggled), + chooser); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Name"), + renderer, + "text", ALERT_DISPLAY_COL, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Type"), + renderer, + "text", ALERT_SOUND_TYPE_COL, + NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + return treeview; +} + +static int +get_file_type (const char *sound_name, + char **linked_name) +{ + char *name, *filename; + + *linked_name = NULL; + + name = g_strdup_printf ("%s.disabled", sound_name); + filename = custom_theme_dir_path (name); + g_free (name); + + if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) != FALSE) { + g_free (filename); + return SOUND_TYPE_OFF; + } + g_free (filename); + + /* We only check for .ogg files because those are the + * only ones we create */ + name = g_strdup_printf ("%s.ogg", sound_name); + filename = custom_theme_dir_path (name); + g_free (name); + + if (g_file_test (filename, G_FILE_TEST_IS_SYMLINK) != FALSE) { + *linked_name = g_file_read_link (filename, NULL); + g_free (filename); + return SOUND_TYPE_CUSTOM; + } + g_free (filename); + + return SOUND_TYPE_BUILTIN; +} + +static void +update_alerts_from_theme_name (GvcSoundThemeChooser *chooser, + const gchar *name) +{ + if (strcmp (name, CUSTOM_THEME_NAME) != 0) { + /* reset alert to default */ + update_alert (chooser, DEFAULT_ALERT_ID); + } else { + int sound_type; + char *linkname; + + linkname = NULL; + sound_type = get_file_type ("bell-terminal", &linkname); + g_debug ("Found link: %s", linkname); + if (sound_type == SOUND_TYPE_CUSTOM) { + update_alert (chooser, linkname); + } + } +} + +static void +update_theme (GvcSoundThemeChooser *chooser) +{ + char *theme_name; + gboolean events_enabled; + gboolean feedback_enabled; + + feedback_enabled = g_settings_get_boolean (chooser->priv->sound_settings, INPUT_SOUNDS_KEY); + set_input_feedback_enabled (chooser, feedback_enabled); + + events_enabled = g_settings_get_boolean (chooser->priv->sound_settings, EVENT_SOUNDS_KEY); + if (events_enabled) { + theme_name = g_settings_get_string (chooser->priv->sound_settings, SOUND_THEME_KEY); + } else { + theme_name = g_strdup (NO_SOUNDS_THEME_NAME); + } + + gtk_widget_set_sensitive (chooser->priv->selection_box, events_enabled); + gtk_widget_set_sensitive (chooser->priv->click_feedback_button, events_enabled); + + set_combox_for_theme_name (chooser, theme_name); + + update_alerts_from_theme_name (chooser, theme_name); + + g_free (theme_name); +} + +static void +gvc_sound_theme_chooser_class_init (GvcSoundThemeChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gvc_sound_theme_chooser_dispose; + + g_type_class_add_private (klass, sizeof (GvcSoundThemeChooserPrivate)); +} + +static void +on_click_feedback_toggled (GtkToggleButton *button, + GvcSoundThemeChooser *chooser) +{ + gboolean enabled; + + enabled = gtk_toggle_button_get_active (button); + + g_settings_set_boolean (chooser->priv->sound_settings, INPUT_SOUNDS_KEY, enabled); +} + +static void +on_key_changed (GSettings *settings, + gchar *key, + GvcSoundThemeChooser *chooser) +{ + if (!strcmp (key, EVENT_SOUNDS_KEY) || + !strcmp (key, SOUND_THEME_KEY) || + !strcmp (key, INPUT_SOUNDS_KEY)) + update_theme (chooser); +} + +#if !GTK_CHECK_VERSION (3, 0, 0) +static void +constrain_list_size (GtkWidget *widget, + GtkRequisition *requisition, + GtkWidget *to_size) +{ + GtkRequisition req; + int max_height; + + /* Constrain height to be the tree height up to a max */ + max_height = (gdk_screen_get_height (gtk_widget_get_screen (widget))) / 4; + + gtk_widget_size_request (to_size, &req); + + requisition->height = MIN (req.height, max_height); +} +#endif + +static void +setup_list_size_constraint (GtkWidget *widget, + GtkWidget *to_size) +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkRequisition req; + int max_height; + + /* Constrain height to be the tree height up to a max */ + max_height = (gdk_screen_get_height (gtk_widget_get_screen (widget))) / 4; + + // XXX this doesn't work + gtk_widget_get_preferred_size (to_size, NULL, &req); + + gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (widget), + MIN (req.height, max_height)); +#else + g_signal_connect (G_OBJECT (widget), + "size-request", + G_CALLBACK (constrain_list_size), + to_size); +#endif +} + +static void +gvc_sound_theme_chooser_init (GvcSoundThemeChooser *chooser) +{ + GtkWidget *box; + GtkWidget *label; + GtkWidget *scrolled_window; + GtkWidget *alignment; + gchar *str; + + chooser->priv = GVC_SOUND_THEME_CHOOSER_GET_PRIVATE (chooser); + +#if GTK_CHECK_VERSION (3, 0, 0) + chooser->priv->theme_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); +#else + chooser->priv->theme_box = gtk_hbox_new (FALSE, 0); +#endif + + gtk_box_pack_start (GTK_BOX (chooser), + chooser->priv->theme_box, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic (_("Sound _theme:")); + gtk_box_pack_start (GTK_BOX (chooser->priv->theme_box), label, FALSE, FALSE, 0); + chooser->priv->combo_box = gtk_combo_box_new (); + gtk_box_pack_start (GTK_BOX (chooser->priv->theme_box), chooser->priv->combo_box, FALSE, FALSE, 6); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser->priv->combo_box); + + chooser->priv->sound_settings = g_settings_new (KEY_SOUNDS_SCHEMA); + + str = g_strdup_printf ("%s", _("C_hoose an alert sound:")); + chooser->priv->selection_box = box = gtk_frame_new (str); + g_free (str); + + label = gtk_frame_get_label_widget (GTK_FRAME (box)); + gtk_label_set_use_underline (GTK_LABEL (label), TRUE); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); + + alignment = gtk_alignment_new (0, 0, 1, 1); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); + gtk_container_add (GTK_CONTAINER (alignment), box); + gtk_box_pack_start (GTK_BOX (chooser), alignment, TRUE, TRUE, 6); + + alignment = gtk_alignment_new (0, 0, 1, 1); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); + gtk_container_add (GTK_CONTAINER (box), alignment); + + chooser->priv->treeview = create_alert_treeview (chooser); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser->priv->treeview); + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_SHADOW_IN); + + gtk_container_add (GTK_CONTAINER (scrolled_window), chooser->priv->treeview); + gtk_container_add (GTK_CONTAINER (alignment), scrolled_window); + + chooser->priv->click_feedback_button = gtk_check_button_new_with_mnemonic (_("Enable _window and button sounds")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser->priv->click_feedback_button), + g_settings_get_boolean (chooser->priv->sound_settings, INPUT_SOUNDS_KEY)); + + gtk_box_pack_start (GTK_BOX (chooser), + chooser->priv->click_feedback_button, + FALSE, FALSE, 0); + + g_signal_connect (G_OBJECT (chooser->priv->click_feedback_button), + "toggled", + G_CALLBACK (on_click_feedback_toggled), + chooser); + g_signal_connect (G_OBJECT (chooser->priv->sound_settings), + "changed", + G_CALLBACK (on_key_changed), + chooser); + + setup_theme_selector (chooser); + update_theme (chooser); + + setup_list_size_constraint (scrolled_window, chooser->priv->treeview); +} + +static void +gvc_sound_theme_chooser_dispose (GObject *object) +{ + GvcSoundThemeChooser *chooser; + + chooser = GVC_SOUND_THEME_CHOOSER (object); + + g_clear_object (&chooser->priv->sound_settings); + + G_OBJECT_CLASS (gvc_sound_theme_chooser_parent_class)->dispose (object); +} + +GtkWidget * +gvc_sound_theme_chooser_new (void) +{ + return g_object_new (GVC_TYPE_SOUND_THEME_CHOOSER, + "spacing", 6, +#if GTK_CHECK_VERSION (3, 0, 0) + "orientation", GTK_ORIENTATION_VERTICAL, +#endif + NULL); +} diff --git a/mate-volume-control/gvc-sound-theme-chooser.h b/mate-volume-control/gvc-sound-theme-chooser.h new file mode 100644 index 0000000..c7bfac0 --- /dev/null +++ b/mate-volume-control/gvc-sound-theme-chooser.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_SOUND_THEME_CHOOSER_H +#define __GVC_SOUND_THEME_CHOOSER_H + +#include + +G_BEGIN_DECLS + +#define GVC_TYPE_SOUND_THEME_CHOOSER (gvc_sound_theme_chooser_get_type ()) +#define GVC_SOUND_THEME_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooser)) +#define GVC_SOUND_THEME_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooserClass)) +#define GVC_IS_SOUND_THEME_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_SOUND_THEME_CHOOSER)) +#define GVC_IS_SOUND_THEME_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_SOUND_THEME_CHOOSER)) +#define GVC_SOUND_THEME_CHOOSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooserClass)) + +typedef struct GvcSoundThemeChooserPrivate GvcSoundThemeChooserPrivate; + +typedef struct +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBox parent; +#else + GtkVBox parent; +#endif + GvcSoundThemeChooserPrivate *priv; +} GvcSoundThemeChooser; + +typedef struct +{ +#if GTK_CHECK_VERSION (3, 0, 0) + GtkBoxClass parent_class; +#else + GtkVBoxClass parent_class; +#endif +} GvcSoundThemeChooserClass; + +GType gvc_sound_theme_chooser_get_type (void); + +GtkWidget * gvc_sound_theme_chooser_new (void); + +G_END_DECLS + +#endif /* __GVC_SOUND_THEME_CHOOSER_H */ diff --git a/mate-volume-control/gvc-speaker-test.c b/mate-volume-control/gvc-speaker-test.c new file mode 100644 index 0000000..e638413 --- /dev/null +++ b/mate-volume-control/gvc-speaker-test.c @@ -0,0 +1,517 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Bastien Nocera + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include "gvc-speaker-test.h" +#include "gvc-utils.h" + +#define GVC_SPEAKER_TEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestPrivate)) + +struct _GvcSpeakerTestPrivate +{ + GArray *controls; + ca_context *canberra; + MateMixerStream *stream; +}; + +enum { + PROP_0, + PROP_STREAM, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void gvc_speaker_test_class_init (GvcSpeakerTestClass *klass); +static void gvc_speaker_test_init (GvcSpeakerTest *test); +static void gvc_speaker_test_dispose (GObject *object); +static void gvc_speaker_test_finalize (GObject *object); + +#if GTK_CHECK_VERSION (3, 4, 0) +G_DEFINE_TYPE (GvcSpeakerTest, gvc_speaker_test, GTK_TYPE_GRID) +#else +G_DEFINE_TYPE (GvcSpeakerTest, gvc_speaker_test, GTK_TYPE_TABLE) +#endif + +typedef struct { + MateMixerChannelPosition position; + guint left; + guint top; +} TablePosition; + +static const TablePosition positions[] = { + /* Position, X, Y */ + { MATE_MIXER_CHANNEL_FRONT_LEFT, 0, 0, }, + { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, 1, 0, }, + { MATE_MIXER_CHANNEL_FRONT_CENTER, 2, 0, }, + { MATE_MIXER_CHANNEL_MONO, 2, 0, }, + { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, 3, 0, }, + { MATE_MIXER_CHANNEL_FRONT_RIGHT, 4, 0, }, + { MATE_MIXER_CHANNEL_SIDE_LEFT, 0, 1, }, + { MATE_MIXER_CHANNEL_SIDE_RIGHT, 4, 1, }, + { MATE_MIXER_CHANNEL_BACK_LEFT, 0, 2, }, + { MATE_MIXER_CHANNEL_BACK_CENTER, 2, 2, }, + { MATE_MIXER_CHANNEL_BACK_RIGHT, 4, 2, }, + { MATE_MIXER_CHANNEL_LFE, 3, 2 } +}; + +MateMixerStream * +gvc_speaker_test_get_stream (GvcSpeakerTest *test) +{ + g_return_val_if_fail (GVC_IS_SPEAKER_TEST (test), NULL); + + return test->priv->stream; +} + +static void +gvc_speaker_test_set_stream (GvcSpeakerTest *test, MateMixerStream *stream) +{ + MateMixerStreamControl *control; + const gchar *name; + guint i; + + name = mate_mixer_stream_get_name (stream); + control = mate_mixer_stream_get_default_control (stream); + + ca_context_change_device (test->priv->canberra, name); + + for (i = 0; i < G_N_ELEMENTS (positions); i++) { + gboolean has_position = + mate_mixer_stream_control_has_channel_position (control, positions[i].position); + + gtk_widget_set_visible (g_array_index (test->priv->controls, GtkWidget *, i), + has_position); + } + + test->priv->stream = g_object_ref (stream); +} + +static void +gvc_speaker_test_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GvcSpeakerTest *self = GVC_SPEAKER_TEST (object); + + switch (prop_id) { + case PROP_STREAM: + gvc_speaker_test_set_stream (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_speaker_test_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GvcSpeakerTest *self = GVC_SPEAKER_TEST (object); + + switch (prop_id) { + case PROP_STREAM: + g_value_set_object (value, self->priv->stream); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_speaker_test_class_init (GvcSpeakerTestClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gvc_speaker_test_dispose; + object_class->finalize = gvc_speaker_test_finalize; + object_class->set_property = gvc_speaker_test_set_property; + object_class->get_property = gvc_speaker_test_get_property; + + properties[PROP_STREAM] = + g_param_spec_object ("stream", + "Stream", + "MateMixer stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (klass, sizeof (GvcSpeakerTestPrivate)); +} + +static const gchar * +sound_name (MateMixerChannelPosition position) +{ + switch (position) { + case MATE_MIXER_CHANNEL_FRONT_LEFT: + return "audio-channel-front-left"; + case MATE_MIXER_CHANNEL_FRONT_RIGHT: + return "audio-channel-front-right"; + case MATE_MIXER_CHANNEL_FRONT_CENTER: + return "audio-channel-front-center"; + case MATE_MIXER_CHANNEL_BACK_LEFT: + return "audio-channel-rear-left"; + case MATE_MIXER_CHANNEL_BACK_RIGHT: + return "audio-channel-rear-right"; + case MATE_MIXER_CHANNEL_BACK_CENTER: + return "audio-channel-rear-center"; + case MATE_MIXER_CHANNEL_LFE: + return "audio-channel-lfe"; + case MATE_MIXER_CHANNEL_SIDE_LEFT: + return "audio-channel-side-left"; + case MATE_MIXER_CHANNEL_SIDE_RIGHT: + return "audio-channel-side-right"; + default: + return NULL; + } +} + +static const gchar * +icon_name (MateMixerChannelPosition position, gboolean playing) +{ + switch (position) { + case MATE_MIXER_CHANNEL_FRONT_LEFT: + return playing + ? "audio-speaker-left-testing" + : "audio-speaker-left"; + case MATE_MIXER_CHANNEL_FRONT_RIGHT: + return playing + ? "audio-speaker-right-testing" + : "audio-speaker-right"; + case MATE_MIXER_CHANNEL_FRONT_CENTER: + return playing + ? "audio-speaker-center-testing" + : "audio-speaker-center"; + case MATE_MIXER_CHANNEL_BACK_LEFT: + return playing + ? "audio-speaker-left-back-testing" + : "audio-speaker-left-back"; + case MATE_MIXER_CHANNEL_BACK_RIGHT: + return playing + ? "audio-speaker-right-back-testing" + : "audio-speaker-right-back"; + case MATE_MIXER_CHANNEL_BACK_CENTER: + return playing + ? "audio-speaker-center-back-testing" + : "audio-speaker-center-back"; + case MATE_MIXER_CHANNEL_LFE: + return playing + ? "audio-subwoofer-testing" + : "audio-subwoofer"; + case MATE_MIXER_CHANNEL_SIDE_LEFT: + return playing + ? "audio-speaker-left-side-testing" + : "audio-speaker-left-side"; + case MATE_MIXER_CHANNEL_SIDE_RIGHT: + return playing + ? "audio-speaker-right-side-testing" + : "audio-speaker-right-side"; + default: + return NULL; + } +} + +static void +update_button (GtkWidget *control) +{ + GtkWidget *button; + GtkWidget *image; + gboolean playing; + MateMixerChannelPosition position; + + button = g_object_get_data (G_OBJECT (control), "button"); + image = g_object_get_data (G_OBJECT (control), "image"); + + position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position")); + playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing")); + + gtk_button_set_label (GTK_BUTTON (button), playing ? _("Stop") : _("Test")); + + gtk_image_set_from_icon_name (GTK_IMAGE (image), + icon_name (position, playing), + GTK_ICON_SIZE_DIALOG); +} + +static gboolean +idle_cb (GtkWidget *control) +{ + if (control != NULL) { + /* This is called in the background thread, hence forward to main thread + * via idle callback */ + g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); + + update_button (control); + } + return FALSE; +} + +static void +finish_cb (ca_context *c, uint32_t id, int error_code, void *userdata) +{ + GtkWidget *control = (GtkWidget *) userdata; + + if (error_code == CA_ERROR_DESTROYED || control == NULL) + return; + + g_idle_add ((GSourceFunc) idle_cb, control); +} + +static void +on_test_button_clicked (GtkButton *button, GtkWidget *control) +{ + gboolean playing; + ca_context *canberra; + + canberra = g_object_get_data (G_OBJECT (control), "canberra"); + + ca_context_cancel (canberra, 1); + + playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing")); + + if (playing) { + g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); + } else { + MateMixerChannelPosition position; + const gchar *name; + ca_proplist *proplist; + + position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position")); + + ca_proplist_create (&proplist); + ca_proplist_sets (proplist, + CA_PROP_MEDIA_ROLE, "test"); + ca_proplist_sets (proplist, + CA_PROP_MEDIA_NAME, + gvc_channel_position_to_pretty_string (position)); + ca_proplist_sets (proplist, + CA_PROP_CANBERRA_FORCE_CHANNEL, + gvc_channel_position_to_pulse_string (position)); + + ca_proplist_sets (proplist, CA_PROP_CANBERRA_ENABLE, "1"); + + name = sound_name (position); + if (name != NULL) { + ca_proplist_sets (proplist, CA_PROP_EVENT_ID, name); + playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0; + } + + if (!playing) { + ca_proplist_sets (proplist, CA_PROP_EVENT_ID, "audio-test-signal"); + playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0; + } + + if (!playing) { + ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "bell-window-system"); + playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0; + } + + g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (playing)); + } + + update_button (control); +} + +static GtkWidget * +create_control (ca_context *canberra, MateMixerChannelPosition position) +{ + GtkWidget *control; + GtkWidget *box; + GtkWidget *label; + GtkWidget *image; + GtkWidget *test_button; + const gchar *name; + +#if GTK_CHECK_VERSION (3, 0, 0) + control = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); +#else + control = gtk_vbox_new (FALSE, 6); + box = gtk_hbox_new (FALSE, 0); +#endif + g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); + g_object_set_data (G_OBJECT (control), "position", GINT_TO_POINTER (position)); + g_object_set_data (G_OBJECT (control), "canberra", canberra); + + name = icon_name (position, FALSE); + if (name == NULL) + name = "audio-volume-medium"; + + image = gtk_image_new_from_icon_name (name, GTK_ICON_SIZE_DIALOG); + g_object_set_data (G_OBJECT (control), "image", image); + gtk_box_pack_start (GTK_BOX (control), image, FALSE, FALSE, 0); + + label = gtk_label_new (gvc_channel_position_to_pretty_string (position)); + gtk_box_pack_start (GTK_BOX (control), label, FALSE, FALSE, 0); + + test_button = gtk_button_new_with_label (_("Test")); + g_signal_connect (G_OBJECT (test_button), + "clicked", + G_CALLBACK (on_test_button_clicked), + control); + + g_object_set_data (G_OBJECT (control), "button", test_button); + + gtk_box_pack_start (GTK_BOX (box), test_button, TRUE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (control), box, FALSE, FALSE, 0); + + gtk_widget_show_all (control); + + return control; +} + +static void +create_controls (GvcSpeakerTest *test) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (positions); i++) { + GtkWidget *control = create_control (test->priv->canberra, positions[i].position); + +#if GTK_CHECK_VERSION (3, 4, 0) + gtk_grid_attach (GTK_GRID (test), + control, + positions[i].left, + positions[i].top, + 1, 1); +#else + gtk_table_attach (GTK_TABLE (test), + control, + positions[i].left, + positions[i].left + 1, + positions[i].top, + positions[i].top + 1, + GTK_EXPAND, GTK_EXPAND, 0, 0); +#endif + g_array_insert_val (test->priv->controls, i, control); + } +} + +static void +gvc_speaker_test_init (GvcSpeakerTest *test) +{ + GtkWidget *face; + + test->priv = GVC_SPEAKER_TEST_GET_PRIVATE (test); + + gtk_container_set_border_width (GTK_CONTAINER (test), 12); + + face = gtk_image_new_from_icon_name ("face-smile", GTK_ICON_SIZE_DIALOG); + +#if GTK_CHECK_VERSION (3, 4, 0) + gtk_grid_attach (GTK_GRID (test), + face, + 1, 1, + 3, 1); + + + gtk_grid_set_baseline_row (GTK_GRID (test), 1); +#else + gtk_table_attach (GTK_TABLE (test), + face, + 2, 3, 1, 2, + GTK_EXPAND, + GTK_EXPAND, + 0, 0); +#endif + gtk_widget_show (face); + + ca_context_create (&test->priv->canberra); + + /* The test sounds are played for a single channel, set up using the + * FORCE_CHANNEL property of libcanberra; this property is only supported + * in the PulseAudio backend, so avoid other backends completely */ + ca_context_set_driver (test->priv->canberra, "pulse"); + + ca_context_change_props (test->priv->canberra, + CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", + CA_PROP_APPLICATION_NAME, _("Volume Control"), + CA_PROP_APPLICATION_VERSION, VERSION, + CA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control", + NULL); + + test->priv->controls = g_array_new (FALSE, FALSE, sizeof (GtkWidget *)); + + create_controls (test); +} + +static void +gvc_speaker_test_dispose (GObject *object) +{ + GvcSpeakerTest *test; + + test = GVC_SPEAKER_TEST (object); + + g_clear_object (&test->priv->stream); + + G_OBJECT_CLASS (gvc_speaker_test_parent_class)->dispose (object); +} + +static void +gvc_speaker_test_finalize (GObject *object) +{ + GvcSpeakerTest *test; + + test = GVC_SPEAKER_TEST (object); + + ca_context_destroy (test->priv->canberra); + + G_OBJECT_CLASS (gvc_speaker_test_parent_class)->finalize (object); +} + +GtkWidget * +gvc_speaker_test_new (MateMixerStream *stream) +{ + GObject *test; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + + test = g_object_new (GVC_TYPE_SPEAKER_TEST, + "row-spacing", 6, + "column-spacing", 6, +#if GTK_CHECK_VERSION (3, 4, 0) + "row-homogeneous", TRUE, + "column-homogeneous", TRUE, +#else + "homogeneous", TRUE, + "n-rows", 3, + "n-columns", 5, +#endif + "stream", stream, + NULL); + + return GTK_WIDGET (test); +} diff --git a/mate-volume-control/gvc-speaker-test.h b/mate-volume-control/gvc-speaker-test.h new file mode 100644 index 0000000..1c1546d --- /dev/null +++ b/mate-volume-control/gvc-speaker-test.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_SPEAKER_TEST_H +#define __GVC_SPEAKER_TEST_H + +#include +#include +#include + +#include + +G_BEGIN_DECLS + +#define GVC_TYPE_SPEAKER_TEST (gvc_speaker_test_get_type ()) +#define GVC_SPEAKER_TEST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTest)) +#define GVC_SPEAKER_TEST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestClass)) +#define GVC_IS_SPEAKER_TEST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_SPEAKER_TEST)) +#define GVC_IS_SPEAKER_TEST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_SPEAKER_TEST)) +#define GVC_SPEAKER_TEST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestClass)) + +typedef struct _GvcSpeakerTest GvcSpeakerTest; +typedef struct _GvcSpeakerTestClass GvcSpeakerTestClass; +typedef struct _GvcSpeakerTestPrivate GvcSpeakerTestPrivate; + +struct _GvcSpeakerTest +{ +#if GTK_CHECK_VERSION (3, 4, 0) + GtkGrid parent; +#else + GtkTable parent; +#endif + GvcSpeakerTestPrivate *priv; +}; + +struct _GvcSpeakerTestClass +{ +#if GTK_CHECK_VERSION (3, 4, 0) + GtkGridClass parent_class; +#else + GtkTableClass parent_class; +#endif +}; + +GType gvc_speaker_test_get_type (void) G_GNUC_CONST; + +GtkWidget * gvc_speaker_test_new (MateMixerStream *stream); + +MateMixerStream * gvc_speaker_test_get_stream (GvcSpeakerTest *test); + +G_END_DECLS + +#endif /* __GVC_SPEAKER_TEST_H */ diff --git a/mate-volume-control/gvc-stream-status-icon.c b/mate-volume-control/gvc-stream-status-icon.c new file mode 100644 index 0000000..3ede4b1 --- /dev/null +++ b/mate-volume-control/gvc-stream-status-icon.c @@ -0,0 +1,805 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include + +#include + +#define MATE_DESKTOP_USE_UNSTABLE_API +#include + +#include "gvc-channel-bar.h" +#include "gvc-stream-status-icon.h" + +#define GVC_STREAM_STATUS_ICON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIconPrivate)) + +struct _GvcStreamStatusIconPrivate +{ + gchar **icon_names; + GtkWidget *dock; + GtkWidget *bar; + guint current_icon; + gchar *display_name; + MateMixerStreamControl *control; +}; + +enum +{ + PROP_0, + PROP_CONTROL, + PROP_DISPLAY_NAME, + PROP_ICON_NAMES, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass); +static void gvc_stream_status_icon_init (GvcStreamStatusIcon *stream_status_icon); +static void gvc_stream_status_icon_finalize (GObject *object); + +G_DEFINE_TYPE (GvcStreamStatusIcon, gvc_stream_status_icon, GTK_TYPE_STATUS_ICON) + +static gboolean +popup_dock (GvcStreamStatusIcon *icon, guint time) +{ + GdkRectangle area; + GtkOrientation orientation; + GdkDisplay *display; + GdkScreen *screen; + int x; + int y; + int monitor_num; + GdkRectangle monitor; + GtkRequisition dock_req; + + screen = gtk_status_icon_get_screen (GTK_STATUS_ICON (icon)); + + if (gtk_status_icon_get_geometry (GTK_STATUS_ICON (icon), + &screen, + &area, + &orientation) == FALSE) { + g_warning ("Unable to determine geometry of status icon"); + return FALSE; + } + + /* position roughly */ + gtk_window_set_screen (GTK_WINDOW (icon->priv->dock), screen); + gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar), + 1 - orientation); + + monitor_num = gdk_screen_get_monitor_at_point (screen, area.x, area.y); + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + + gtk_container_foreach (GTK_CONTAINER (icon->priv->dock), + (GtkCallback) gtk_widget_show_all, NULL); +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_get_preferred_size (icon->priv->dock, &dock_req, NULL); +#else + gtk_widget_size_request (icon->priv->dock, &dock_req); +#endif + + if (orientation == GTK_ORIENTATION_VERTICAL) { + if (area.x + area.width + dock_req.width <= monitor.x + monitor.width) + x = area.x + area.width; + else + x = area.x - dock_req.width; + + if (area.y + dock_req.height <= monitor.y + monitor.height) + y = area.y; + else + y = monitor.y + monitor.height - dock_req.height; + } else { + if (area.y + area.height + dock_req.height <= monitor.y + monitor.height) + y = area.y + area.height; + else + y = area.y - dock_req.height; + + if (area.x + dock_req.width <= monitor.x + monitor.width) + x = area.x; + else + x = monitor.x + monitor.width - dock_req.width; + } + + gtk_window_move (GTK_WINDOW (icon->priv->dock), x, y); + + /* Without this, the popup window appears as a square after changing + * the orientation */ + gtk_window_resize (GTK_WINDOW (icon->priv->dock), 1, 1); + + gtk_widget_show_all (icon->priv->dock); + + /* Grab focus */ + gtk_grab_add (icon->priv->dock); + + display = gtk_widget_get_display (icon->priv->dock); + +#if GTK_CHECK_VERSION (3, 0, 0) + do { + GdkDeviceManager *manager = gdk_display_get_device_manager (display); + + if (gdk_device_grab (gdk_device_manager_get_client_pointer (manager), + gtk_widget_get_window (icon->priv->dock), + GDK_OWNERSHIP_NONE, + TRUE, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_SCROLL_MASK, + NULL, + time) != GDK_GRAB_SUCCESS) { + gtk_grab_remove (icon->priv->dock); + gtk_widget_hide (icon->priv->dock); + } + } while (0); +#else + if (gdk_pointer_grab (gtk_widget_get_window (icon->priv->dock), + TRUE, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_SCROLL_MASK, + NULL, NULL, + time) != GDK_GRAB_SUCCESS) { + gtk_grab_remove (icon->priv->dock); + gtk_widget_hide (icon->priv->dock); + return FALSE; + } + + if (gdk_keyboard_grab (gtk_widget_get_window (icon->priv->dock), + TRUE, + time) != GDK_GRAB_SUCCESS) { + gdk_display_pointer_ungrab (display, time); + gtk_grab_remove (icon->priv->dock); + gtk_widget_hide (icon->priv->dock); + return FALSE; + } +#endif + gtk_widget_grab_focus (icon->priv->dock); + + return TRUE; +} + +static void +on_status_icon_activate (GtkStatusIcon *status_icon, GvcStreamStatusIcon *icon) +{ + popup_dock (icon, GDK_CURRENT_TIME); +} + +static gboolean +on_status_icon_button_press (GtkStatusIcon *status_icon, + GdkEventButton *event, + GvcStreamStatusIcon *icon) +{ + /* Middle click acts as mute/unmute */ + if (event->button == 2) { + gboolean is_muted = mate_mixer_stream_control_get_mute (icon->priv->control); + + mate_mixer_stream_control_set_mute (icon->priv->control, !is_muted); + return TRUE; + } + return FALSE; +} + +static void +on_menu_mute_toggled (GtkMenuItem *item, GvcStreamStatusIcon *icon) +{ + gboolean is_muted; + + is_muted = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)); + + mate_mixer_stream_control_set_mute (icon->priv->control, is_muted); +} + +static void +on_menu_activate_open_volume_control (GtkMenuItem *item, + GvcStreamStatusIcon *icon) +{ + GError *error = NULL; + + mate_gdk_spawn_command_line_on_screen (gtk_widget_get_screen (icon->priv->dock), + "mate-volume-control", + &error); + + if (error != NULL) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Failed to start Sound Preferences: %s"), + error->message); + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + gtk_widget_show (dialog); + g_error_free (error); + } +} + +static void +on_status_icon_popup_menu (GtkStatusIcon *status_icon, + guint button, + guint activate_time, + GvcStreamStatusIcon *icon) +{ + GtkWidget *menu; + GtkWidget *item; + + menu = gtk_menu_new (); + item = gtk_check_menu_item_new_with_mnemonic (_("_Mute")); + + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), + mate_mixer_stream_control_get_mute (icon->priv->control)); + g_signal_connect (G_OBJECT (item), + "toggled", + G_CALLBACK (on_menu_mute_toggled), + icon); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +#if GTK_CHECK_VERSION (3, 10, 0) + item = gtk_menu_item_new_with_mnemonic (_("_Sound Preferences")); +#else + item = gtk_image_menu_item_new_with_mnemonic (_("_Sound Preferences")); + + do { + GtkWidget *image; + + image = gtk_image_new_from_icon_name ("multimedia-volume-control", + GTK_ICON_SIZE_MENU); + + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + } while (0); +#endif + g_signal_connect (G_OBJECT (item), + "activate", + G_CALLBACK (on_menu_activate_open_volume_control), + icon); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + gtk_widget_show_all (menu); + gtk_menu_popup (GTK_MENU (menu), + NULL, + NULL, + gtk_status_icon_position_menu, + status_icon, + button, + activate_time); +} + +static gboolean +on_status_icon_scroll_event (GtkStatusIcon *status_icon, + GdkEventScroll *event, + GvcStreamStatusIcon *icon) +{ + return gvc_channel_bar_scroll (GVC_CHANNEL_BAR (icon->priv->bar), event->direction); +} + +static void +gvc_icon_release_grab (GvcStreamStatusIcon *icon, GdkEventButton *event) +{ +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_device_ungrab (event->device, event->time); +#else + GdkDisplay *display; + + display = gtk_widget_get_display (GTK_WIDGET (icon->priv->dock)); + + gdk_display_keyboard_ungrab (display, event->time); + gdk_display_pointer_ungrab (display, event->time); +#endif + gtk_grab_remove (icon->priv->dock); + + /* Hide again */ + gtk_widget_hide (icon->priv->dock); +} + +static gboolean +on_dock_button_press (GtkWidget *widget, + GdkEventButton *event, + GvcStreamStatusIcon *icon) +{ + if (event->type == GDK_BUTTON_PRESS) { + gvc_icon_release_grab (icon, event); + return TRUE; + } + + return FALSE; +} + +static void +popdown_dock (GvcStreamStatusIcon *icon) +{ + GdkDisplay *display; + + display = gtk_widget_get_display (icon->priv->dock); + +#if GTK_CHECK_VERSION (3, 0, 0) + GdkDeviceManager *manager = gdk_display_get_device_manager (display); + + gdk_device_ungrab (gdk_device_manager_get_client_pointer (manager), + GDK_CURRENT_TIME); +#else + gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME); + gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); + gtk_grab_remove (icon->priv->dock); +#endif + /* Hide again */ + gtk_widget_hide (icon->priv->dock); +} + +/* This is called when the grab is broken for either the dock, or the scale */ +static void +gvc_icon_grab_notify (GvcStreamStatusIcon *icon, gboolean was_grabbed) +{ + if (was_grabbed != FALSE) + return; + + if (gtk_widget_has_grab (icon->priv->dock) == FALSE) + return; + + if (gtk_widget_is_ancestor (gtk_grab_get_current (), icon->priv->dock)) + return; + + popdown_dock (icon); +} + +static void +on_dock_grab_notify (GtkWidget *widget, + gboolean was_grabbed, + GvcStreamStatusIcon *icon) +{ + gvc_icon_grab_notify (icon, was_grabbed); +} + +static gboolean +on_dock_grab_broken_event (GtkWidget *widget, + gboolean was_grabbed, + GvcStreamStatusIcon *icon) +{ + gvc_icon_grab_notify (icon, FALSE); + return FALSE; +} + +static gboolean +on_dock_key_release (GtkWidget *widget, + GdkEventKey *event, + GvcStreamStatusIcon *icon) +{ + if (event->keyval == GDK_KEY_Escape) { + popdown_dock (icon); + return TRUE; + } + return TRUE; +} + +static gboolean +on_dock_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcStreamStatusIcon *icon) +{ + /* Forward event to the status icon */ + on_status_icon_scroll_event (NULL, event, icon); + return TRUE; +} + +static void +update_icon (GvcStreamStatusIcon *icon) +{ + guint volume = 0; + gdouble decibel = 0; + guint normal = 0; + gboolean muted = FALSE; + guint n = 0; + gchar *markup; + const gchar *description; + MateMixerStreamControlFlags flags; + + if (icon->priv->control == NULL) { + /* Do not bother creating a tooltip for an unusable icon as it + * has no practical use */ + gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), FALSE); + return; + } else + gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), TRUE); + + flags = mate_mixer_stream_control_get_flags (icon->priv->control); + + if (flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) + muted = mate_mixer_stream_control_get_mute (icon->priv->control); + + if (flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { + volume = mate_mixer_stream_control_get_volume (icon->priv->control); + normal = mate_mixer_stream_control_get_normal_volume (icon->priv->control); + + /* Select an icon, they are expected to be sorted, the lowest index being + * the mute icon and the rest being volume increments */ + if (volume <= 0 || muted) + n = 0; + else + n = CLAMP (3 * volume / normal + 1, 1, 3); + } + if (flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) + decibel = mate_mixer_stream_control_get_decibel (icon->priv->control); + + /* Apparently status icon will reset icon even if it doesn't change */ + if (icon->priv->current_icon != n) { + gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), + icon->priv->icon_names[n]); + icon->priv->current_icon = n; + } + + description = mate_mixer_stream_control_get_label (icon->priv->control); + + if (muted) { + markup = g_strdup_printf ("%s: %s\n%s", + icon->priv->display_name, + _("Muted"), + description); + } else if (flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { + gdouble display_volume = 100 * volume / normal; + + if (flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) { + if (decibel > -MATE_MIXER_INFINITY) { + markup = g_strdup_printf ("%s: %.0f%%\n" + "%0.2f dB\n%s", + icon->priv->display_name, + display_volume, + decibel, + description); + } else { + markup = g_strdup_printf ("%s: %.0f%%\n" + "-∞ dB\n%s", + icon->priv->display_name, + display_volume, + description); + } + } else { + markup = g_strdup_printf ("%s: %.0f%%\n%s", + icon->priv->display_name, + display_volume, + description); + } + } else { + markup = g_strdup_printf ("%s\n%s", + icon->priv->display_name, + description); + } + + gtk_status_icon_set_tooltip_markup (GTK_STATUS_ICON (icon), markup); + + g_free (markup); +} + +void +gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon *icon, + const gchar **names) +{ + g_return_if_fail (GVC_IS_STREAM_STATUS_ICON (icon)); + g_return_if_fail (names != NULL && *names != NULL); + + if (G_UNLIKELY (g_strv_length ((gchar **) names) != 4)) { + g_warn_if_reached (); + return; + } + + g_strfreev (icon->priv->icon_names); + + icon->priv->icon_names = g_strdupv ((gchar **) names); + + /* Set the first icon as the initial one, the icon may be immediately + * updated or not depending on whether a stream is available */ + gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), names[0]); + update_icon (icon); + + g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_ICON_NAMES]); +} + +static void +on_stream_control_volume_notify (MateMixerStreamControl *control, + GParamSpec *pspec, + GvcStreamStatusIcon *icon) +{ + update_icon (icon); +} + +static void +on_stream_control_mute_notify (MateMixerStreamControl *control, + GParamSpec *pspec, + GvcStreamStatusIcon *icon) +{ + update_icon (icon); +} + +void +gvc_stream_status_icon_set_display_name (GvcStreamStatusIcon *icon, + const gchar *name) +{ + g_return_if_fail (GVC_STREAM_STATUS_ICON (icon)); + + g_free (icon->priv->display_name); + + icon->priv->display_name = g_strdup (name); + update_icon (icon); + + g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_DISPLAY_NAME]); +} + +void +gvc_stream_status_icon_set_control (GvcStreamStatusIcon *icon, + MateMixerStreamControl *control) +{ + g_return_if_fail (GVC_STREAM_STATUS_ICON (icon)); + + if (icon->priv->control == control) + return; + + if (control != NULL) + g_object_ref (control); + + if (icon->priv->control != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (icon->priv->control), + G_CALLBACK (on_stream_control_volume_notify), + icon); + g_signal_handlers_disconnect_by_func (G_OBJECT (icon->priv->control), + G_CALLBACK (on_stream_control_mute_notify), + icon); + + g_object_unref (icon->priv->control); + } + + icon->priv->control = control; + + if (icon->priv->control != NULL) { + g_signal_connect (G_OBJECT (icon->priv->control), + "notify::volume", + G_CALLBACK (on_stream_control_volume_notify), + icon); + g_signal_connect (G_OBJECT (icon->priv->control), + "notify::mute", + G_CALLBACK (on_stream_control_mute_notify), + icon); + + // XXX when no stream set some default icon and "unset" dock + update_icon (icon); + } + + gvc_channel_bar_set_control (GVC_CHANNEL_BAR (icon->priv->bar), icon->priv->control); + + g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_CONTROL]); +} + +static void +gvc_stream_status_icon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object); + + switch (prop_id) { + case PROP_CONTROL: + gvc_stream_status_icon_set_control (self, g_value_get_object (value)); + break; + case PROP_DISPLAY_NAME: + gvc_stream_status_icon_set_display_name (self, g_value_get_string (value)); + break; + case PROP_ICON_NAMES: + gvc_stream_status_icon_set_icon_names (self, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_stream_status_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object); + + switch (prop_id) { + case PROP_CONTROL: + g_value_set_object (value, self->priv->control); + break; + case PROP_DISPLAY_NAME: + g_value_set_string (value, self->priv->display_name); + break; + case PROP_ICON_NAMES: + g_value_set_boxed (value, self->priv->icon_names); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gvc_stream_status_icon_dispose (GObject *object) +{ + GvcStreamStatusIcon *icon = GVC_STREAM_STATUS_ICON (object); + + if (icon->priv->dock != NULL) { + gtk_widget_destroy (icon->priv->dock); + icon->priv->dock = NULL; + } + + g_clear_object (&icon->priv->control); + + G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->dispose (object); +} + +static void +gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gvc_stream_status_icon_finalize; + object_class->dispose = gvc_stream_status_icon_dispose; + object_class->set_property = gvc_stream_status_icon_set_property; + object_class->get_property = gvc_stream_status_icon_get_property; + + properties[PROP_CONTROL] = + g_param_spec_object ("control", + "Control", + "MateMixer stream control", + MATE_MIXER_TYPE_STREAM_CONTROL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DISPLAY_NAME] = + g_param_spec_string ("display-name", + "Display name", + "Name to display for this stream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ICON_NAMES] = + g_param_spec_boxed ("icon-names", + "Icon names", + "Name of icon to display for this stream", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (klass, sizeof (GvcStreamStatusIconPrivate)); +} + +static void +on_status_icon_visible_notify (GvcStreamStatusIcon *icon) +{ + if (gtk_status_icon_get_visible (GTK_STATUS_ICON (icon)) == FALSE) + gtk_widget_hide (icon->priv->dock); +} + +static void +gvc_stream_status_icon_init (GvcStreamStatusIcon *icon) +{ + GtkWidget *frame; + GtkWidget *box; + + icon->priv = GVC_STREAM_STATUS_ICON_GET_PRIVATE (icon); + + g_signal_connect (G_OBJECT (icon), + "activate", + G_CALLBACK (on_status_icon_activate), + icon); + g_signal_connect (G_OBJECT (icon), + "button-press-event", + G_CALLBACK (on_status_icon_button_press), + icon); + g_signal_connect (G_OBJECT (icon), + "popup-menu", + G_CALLBACK (on_status_icon_popup_menu), + icon); + g_signal_connect (G_OBJECT (icon), + "scroll-event", + G_CALLBACK (on_status_icon_scroll_event), + icon); + g_signal_connect (G_OBJECT (icon), + "notify::visible", + G_CALLBACK (on_status_icon_visible_notify), + NULL); + + /* Create the dock window */ + icon->priv->dock = gtk_window_new (GTK_WINDOW_POPUP); + + gtk_window_set_decorated (GTK_WINDOW (icon->priv->dock), FALSE); + + g_signal_connect (G_OBJECT (icon->priv->dock), + "button-press-event", + G_CALLBACK (on_dock_button_press), + icon); + g_signal_connect (G_OBJECT (icon->priv->dock), + "key-release-event", + G_CALLBACK (on_dock_key_release), + icon); + g_signal_connect (G_OBJECT (icon->priv->dock), + "scroll-event", + G_CALLBACK (on_dock_scroll_event), + icon); + g_signal_connect (G_OBJECT (icon->priv->dock), + "grab-notify", + G_CALLBACK (on_dock_grab_notify), + icon); + g_signal_connect (G_OBJECT (icon->priv->dock), + "grab-broken-event", + G_CALLBACK (on_dock_grab_broken_event), + icon); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); + gtk_container_add (GTK_CONTAINER (icon->priv->dock), frame); + + icon->priv->bar = gvc_channel_bar_new (NULL); + + gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar), + GTK_ORIENTATION_VERTICAL); + +#if GTK_CHECK_VERSION (3, 0, 0) + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); +#else + box = gtk_vbox_new (FALSE, 6); +#endif + + gtk_container_set_border_width (GTK_CONTAINER (box), 2); + gtk_container_add (GTK_CONTAINER (frame), box); + + gtk_box_pack_start (GTK_BOX (box), icon->priv->bar, TRUE, FALSE, 0); +} + +static void +gvc_stream_status_icon_finalize (GObject *object) +{ + GvcStreamStatusIcon *icon; + + icon = GVC_STREAM_STATUS_ICON (object); + + g_strfreev (icon->priv->icon_names); + + G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->finalize (object); +} + +GvcStreamStatusIcon * +gvc_stream_status_icon_new (MateMixerStreamControl *control, + const gchar **icon_names) +{ + return g_object_new (GVC_TYPE_STREAM_STATUS_ICON, + "control", control, + "icon-names", icon_names, + NULL); +} diff --git a/mate-volume-control/gvc-stream-status-icon.h b/mate-volume-control/gvc-stream-status-icon.h new file mode 100644 index 0000000..7b51801 --- /dev/null +++ b/mate-volume-control/gvc-stream-status-icon.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_STREAM_STATUS_ICON_H +#define __GVC_STREAM_STATUS_ICON_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define GVC_TYPE_STREAM_STATUS_ICON (gvc_stream_status_icon_get_type ()) +#define GVC_STREAM_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIcon)) +#define GVC_STREAM_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIconClass)) +#define GVC_IS_STREAM_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_STREAM_STATUS_ICON)) +#define GVC_IS_STREAM_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_STREAM_STATUS_ICON)) +#define GVC_STREAM_STATUS_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIconClass)) + +typedef struct _GvcStreamStatusIcon GvcStreamStatusIcon; +typedef struct _GvcStreamStatusIconClass GvcStreamStatusIconClass; +typedef struct _GvcStreamStatusIconPrivate GvcStreamStatusIconPrivate; + +struct _GvcStreamStatusIcon +{ + GtkStatusIcon parent; + GvcStreamStatusIconPrivate *priv; +}; + +struct _GvcStreamStatusIconClass +{ + GtkStatusIconClass parent_class; +}; + +GType gvc_stream_status_icon_get_type (void) G_GNUC_CONST; + +GvcStreamStatusIcon * gvc_stream_status_icon_new (MateMixerStreamControl *control, + const gchar **icon_names); + +void gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon *icon, + const gchar **icon_names); +void gvc_stream_status_icon_set_display_name (GvcStreamStatusIcon *icon, + const gchar *display_name); + +void gvc_stream_status_icon_set_control (GvcStreamStatusIcon *icon, + MateMixerStreamControl *control); + +G_END_DECLS + +#endif /* __GVC_STREAM_STATUS_ICON_H */ diff --git a/mate-volume-control/gvc-utils.c b/mate-volume-control/gvc-utils.c new file mode 100644 index 0000000..d45a217 --- /dev/null +++ b/mate-volume-control/gvc-utils.c @@ -0,0 +1,335 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include + +#include + +#include "gvc-utils.h" + +/* libcanberra requires a PulseAudio channel name to be given to its + * CA_PROP_CANBERRA_FORCE_CHANNEL property. + * + * The strings here are copied from PulseAudio source code to avoid depending + * on libpulse. */ +static const gchar *pulse_position[MATE_MIXER_CHANNEL_MAX] = { + [MATE_MIXER_CHANNEL_MONO] = "mono", + [MATE_MIXER_CHANNEL_FRONT_LEFT] = "front-left", + [MATE_MIXER_CHANNEL_FRONT_RIGHT] = "front-right", + [MATE_MIXER_CHANNEL_FRONT_CENTER] = "front-center", + [MATE_MIXER_CHANNEL_LFE] = "lfe", + [MATE_MIXER_CHANNEL_BACK_LEFT] = "rear-left", + [MATE_MIXER_CHANNEL_BACK_RIGHT] = "rear-right", + [MATE_MIXER_CHANNEL_BACK_CENTER] = "rear-center", + [MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER] = "front-left-of-center", + [MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER] = "front-right-of-center", + [MATE_MIXER_CHANNEL_SIDE_LEFT] = "side-left", + [MATE_MIXER_CHANNEL_SIDE_RIGHT] = "side-right", + [MATE_MIXER_CHANNEL_TOP_FRONT_LEFT] = "top-front-left", + [MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT] = "top-front-right", + [MATE_MIXER_CHANNEL_TOP_FRONT_CENTER] = "top-front-center", + [MATE_MIXER_CHANNEL_TOP_CENTER] = "top-center", + [MATE_MIXER_CHANNEL_TOP_BACK_LEFT] = "top-rear-left", + [MATE_MIXER_CHANNEL_TOP_BACK_RIGHT] = "top-rear-right", + [MATE_MIXER_CHANNEL_TOP_BACK_CENTER] = "top-rear-center" +}; + +static const gchar *pretty_position[MATE_MIXER_CHANNEL_MAX] = { + [MATE_MIXER_CHANNEL_UNKNOWN] = N_("Unknown"), + /* Speaker channel names */ + [MATE_MIXER_CHANNEL_MONO] = N_("Mono"), + [MATE_MIXER_CHANNEL_FRONT_LEFT] = N_("Front Left"), + [MATE_MIXER_CHANNEL_FRONT_RIGHT] = N_("Front Right"), + [MATE_MIXER_CHANNEL_FRONT_CENTER] = N_("Front Center"), + [MATE_MIXER_CHANNEL_LFE] = N_("LFE"), + [MATE_MIXER_CHANNEL_BACK_LEFT] = N_("Rear Left"), + [MATE_MIXER_CHANNEL_BACK_RIGHT] = N_("Rear Right"), + [MATE_MIXER_CHANNEL_BACK_CENTER] = N_("Rear Center"), + [MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER] = N_("Front Left of Center"), + [MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER] = N_("Front Right of Center"), + [MATE_MIXER_CHANNEL_SIDE_LEFT] = N_("Side Left"), + [MATE_MIXER_CHANNEL_SIDE_RIGHT] = N_("Side Right"), + [MATE_MIXER_CHANNEL_TOP_FRONT_LEFT] = N_("Top Front Left"), + [MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT] = N_("Top Front Right"), + [MATE_MIXER_CHANNEL_TOP_FRONT_CENTER] = N_("Top Front Center"), + [MATE_MIXER_CHANNEL_TOP_CENTER] = N_("Top Center"), + [MATE_MIXER_CHANNEL_TOP_BACK_LEFT] = N_("Top Rear Left"), + [MATE_MIXER_CHANNEL_TOP_BACK_RIGHT] = N_("Top Rear Right"), + [MATE_MIXER_CHANNEL_TOP_BACK_CENTER] = N_("Top Rear Center") +}; + +const gchar * +gvc_channel_position_to_pulse_string (MateMixerChannelPosition position) +{ + g_return_val_if_fail (position >= 0 && position < MATE_MIXER_CHANNEL_MAX, NULL); + + return pulse_position[position]; +} + +const gchar * +gvc_channel_position_to_pretty_string (MateMixerChannelPosition position) +{ + g_return_val_if_fail (position >= 0 && position < MATE_MIXER_CHANNEL_MAX, NULL); + + return pretty_position[position]; +} + +const gchar * +gvc_channel_map_to_pretty_string (MateMixerStreamControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), NULL); + +#define HAS_POSITION(p) (mate_mixer_stream_control_has_channel_position (control, (p))) + + /* Modeled after PulseAudio 5.0, probably could be extended with other combinations */ + switch (mate_mixer_stream_control_get_num_channels (control)) { + case 1: + if (HAS_POSITION (MATE_MIXER_CHANNEL_MONO)) + return _("Mono"); + break; + case 2: + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT)) + return _("Stereo"); + break; + case 4: + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT)) + return _("Surround 4.0"); + break; + case 5: + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) + return _("Surround 4.1"); + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER)) + return _("Surround 5.0"); + break; + case 6: + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) + return _("Surround 5.1"); + break; + case 8: + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_SIDE_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_SIDE_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) + return _("Surround 7.1"); + break; + } + +#undef HAS_POSITION + + return NULL; +} + +#if GTK_CHECK_VERSION (3, 0, 0) +/* Taken from gtkstyle.c */ +static void rgb_to_hls (gdouble *r, gdouble *g, gdouble *b); +static void hls_to_rgb (gdouble *h, gdouble *l, gdouble *s); + +void +gvc_color_shade (GdkRGBA *a, GdkRGBA *b, gdouble k) +{ + gdouble red; + gdouble green; + gdouble blue; + + red = (gdouble) a->red / 65535.0; + green = (gdouble) a->green / 65535.0; + blue = (gdouble) a->blue / 65535.0; + + rgb_to_hls (&red, &green, &blue); + + green *= k; + if (green > 1.0) + green = 1.0; + else if (green < 0.0) + green = 0.0; + + blue *= k; + if (blue > 1.0) + blue = 1.0; + else if (blue < 0.0) + blue = 0.0; + + hls_to_rgb (&red, &green, &blue); + + b->red = red * 65535.0; + b->green = green * 65535.0; + b->blue = blue * 65535.0; +} + +static void +rgb_to_hls (gdouble *r, gdouble *g, gdouble *b) +{ + gdouble min; + gdouble max; + gdouble red; + gdouble green; + gdouble blue; + gdouble h, l, s; + gdouble delta; + + red = *r; + green = *g; + blue = *b; + + if (red > green) { + if (red > blue) + max = red; + else + max = blue; + + if (green < blue) + min = green; + else + min = blue; + } else { + if (green > blue) + max = green; + else + max = blue; + + if (red < blue) + min = red; + else + min = blue; + } + + l = (max + min) / 2; + s = 0; + h = 0; + + if (max != min) { + if (l <= 0.5) + s = (max - min) / (max + min); + else + s = (max - min) / (2 - max - min); + + delta = max - min; + if (red == max) + h = (green - blue) / delta; + else if (green == max) + h = 2 + (blue - red) / delta; + else if (blue == max) + h = 4 + (red - green) / delta; + + h *= 60; + if (h < 0.0) + h += 360; + } + + *r = h; + *g = l; + *b = s; +} + +static void +hls_to_rgb (gdouble *h, gdouble *l, gdouble *s) +{ + gdouble hue; + gdouble lightness; + gdouble saturation; + gdouble m1, m2; + gdouble r, g, b; + + lightness = *l; + saturation = *s; + + if (lightness <= 0.5) + m2 = lightness * (1 + saturation); + else + m2 = lightness + saturation - lightness * saturation; + m1 = 2 * lightness - m2; + + if (saturation == 0) { + *h = lightness; + *l = lightness; + *s = lightness; + } else { + hue = *h + 120; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + r = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + r = m2; + else if (hue < 240) + r = m1 + (m2 - m1) * (240 - hue) / 60; + else + r = m1; + + hue = *h; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + g = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + g = m2; + else if (hue < 240) + g = m1 + (m2 - m1) * (240 - hue) / 60; + else + g = m1; + + hue = *h - 120; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + b = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + b = m2; + else if (hue < 240) + b = m1 + (m2 - m1) * (240 - hue) / 60; + else + b = m1; + + *h = r; + *l = g; + *s = b; + } +} +#endif diff --git a/mate-volume-control/gvc-utils.h b/mate-volume-control/gvc-utils.h new file mode 100644 index 0000000..4eca7a7 --- /dev/null +++ b/mate-volume-control/gvc-utils.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2014 Michal Ratajsky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GVC_HELPERS_H +#define __GVC_HELPERS_H + +#include + +#include + +G_BEGIN_DECLS + +const gchar *gvc_channel_position_to_pulse_string (MateMixerChannelPosition position); +const gchar *gvc_channel_position_to_pretty_string (MateMixerChannelPosition position); +const gchar *gvc_channel_map_to_pretty_string (MateMixerStreamControl *control); + +#if GTK_CHECK_VERSION (3, 0, 0) +void gvc_color_shade (GdkRGBA *a, + GdkRGBA *b, + gdouble k); +#endif + +G_END_DECLS + +#endif /* __GVC_HELPERS_H */ diff --git a/mate-volume-control/sound-theme-file-utils.c b/mate-volume-control/sound-theme-file-utils.c new file mode 100644 index 0000000..1de9886 --- /dev/null +++ b/mate-volume-control/sound-theme-file-utils.c @@ -0,0 +1,305 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * Copyright (C) 2008 Bastien Nocera + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "sound-theme-file-utils.h" + +#define CUSTOM_THEME_NAME "__custom" + +/* This function needs to be called after each individual + * changeset to the theme */ +void +custom_theme_update_time (void) +{ + char *path; + + path = custom_theme_dir_path (NULL); + utime (path, NULL); + g_free (path); +} + +char * +custom_theme_dir_path (const char *child) +{ + static char *dir = NULL; + const char *data_dir; + + if (dir == NULL) { + data_dir = g_get_user_data_dir (); + dir = g_build_filename (data_dir, "sounds", CUSTOM_THEME_NAME, NULL); + } + if (child == NULL) + return g_strdup (dir); + + return g_build_filename (dir, child, NULL); +} + +static gboolean +directory_delete_recursive (GFile *directory, GError **error) +{ + GFileEnumerator *enumerator; + GFileInfo *info; + gboolean success = TRUE; + + enumerator = g_file_enumerate_children (directory, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (enumerator == NULL) + return FALSE; + + while (success && + (info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { + GFile *child; + + child = g_file_get_child (directory, g_file_info_get_name (info)); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { + success = directory_delete_recursive (child, error); + } + g_object_unref (info); + + if (success) + success = g_file_delete (child, NULL, error); + } + g_file_enumerator_close (enumerator, NULL, NULL); + + if (success) + success = g_file_delete (directory, NULL, error); + + return success; +} + +/** + * capplet_file_delete_recursive : + * @file : + * @error : + * + * A utility routine to delete files and/or directories, + * including non-empty directories. + **/ +static gboolean +capplet_file_delete_recursive (GFile *file, GError **error) +{ + GFileInfo *info; + GFileType type; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (info == NULL) + return FALSE; + + type = g_file_info_get_file_type (info); + g_object_unref (info); + + if (type == G_FILE_TYPE_DIRECTORY) + return directory_delete_recursive (file, error); + else + return g_file_delete (file, NULL, error); +} + +void +delete_custom_theme_dir (void) +{ + char *dir; + GFile *file; + + dir = custom_theme_dir_path (NULL); + file = g_file_new_for_path (dir); + g_free (dir); + capplet_file_delete_recursive (file, NULL); + g_object_unref (file); + + g_debug ("deleted the custom theme dir"); +} + +gboolean +custom_theme_dir_is_empty (void) +{ + char *dir; + GFile *file; + gboolean is_empty; + GFileEnumerator *enumerator; + GFileInfo *info; + GError *error = NULL; + + dir = custom_theme_dir_path (NULL); + file = g_file_new_for_path (dir); + g_free (dir); + + is_empty = TRUE; + + enumerator = g_file_enumerate_children (file, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + if (enumerator == NULL) { + g_warning ("Unable to enumerate files: %s", error->message); + g_error_free (error); + goto out; + } + + while (is_empty && + (info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { + + if (strcmp ("index.theme", g_file_info_get_name (info)) != 0) { + is_empty = FALSE; + } + + g_object_unref (info); + } + g_file_enumerator_close (enumerator, NULL, NULL); + + out: + g_object_unref (file); + + return is_empty; +} + +static void +delete_one_file (const char *sound_name, const char *pattern) +{ + GFile *file; + char *name, *filename; + + name = g_strdup_printf (pattern, sound_name); + filename = custom_theme_dir_path (name); + g_free (name); + file = g_file_new_for_path (filename); + g_free (filename); + capplet_file_delete_recursive (file, NULL); + g_object_unref (file); +} + +void +delete_old_files (const char **sounds) +{ + guint i; + + for (i = 0; sounds[i] != NULL; i++) { + delete_one_file (sounds[i], "%s.ogg"); + } +} + +void +delete_disabled_files (const char **sounds) +{ + guint i; + + for (i = 0; sounds[i] != NULL; i++) + delete_one_file (sounds[i], "%s.disabled"); +} + +static void +create_one_file (GFile *file) +{ + GFileOutputStream* stream; + + stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL); + if (stream != NULL) { + g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, NULL); + g_object_unref (stream); + } +} + +void +add_disabled_file (const char **sounds) +{ + guint i; + + for (i = 0; sounds[i] != NULL; i++) { + GFile *file; + char *name, *filename; + + name = g_strdup_printf ("%s.disabled", sounds[i]); + filename = custom_theme_dir_path (name); + g_free (name); + file = g_file_new_for_path (filename); + g_free (filename); + + create_one_file (file); + g_object_unref (file); + } +} + +void +add_custom_file (const char **sounds, const char *filename) +{ + guint i; + + for (i = 0; sounds[i] != NULL; i++) { + GFile *file; + char *name, *path; + + /* We use *.ogg because it's the first type of file that + * libcanberra looks at */ + name = g_strdup_printf ("%s.ogg", sounds[i]); + path = custom_theme_dir_path (name); + g_free (name); + /* In case there's already a link there, delete it */ + g_unlink (path); + file = g_file_new_for_path (path); + g_free (path); + + /* Create the link */ + g_file_make_symbolic_link (file, filename, NULL, NULL); + g_object_unref (file); + } +} + +void +create_custom_theme (const char *parent) +{ + GKeyFile *keyfile; + char *data; + char *path; + + /* Create the custom directory */ + path = custom_theme_dir_path (NULL); + g_mkdir_with_parents (path, 0755); + g_free (path); + + /* Set the data for index.theme */ + keyfile = g_key_file_new (); + g_key_file_set_string (keyfile, "Sound Theme", "Name", _("Custom")); + g_key_file_set_string (keyfile, "Sound Theme", "Inherits", parent); + g_key_file_set_string (keyfile, "Sound Theme", "Directories", "."); + data = g_key_file_to_data (keyfile, NULL, NULL); + g_key_file_free (keyfile); + + /* Save the index.theme */ + path = custom_theme_dir_path ("index.theme"); + g_file_set_contents (path, data, -1, NULL); + g_free (path); + g_free (data); + + custom_theme_update_time (); +} diff --git a/mate-volume-control/sound-theme-file-utils.h b/mate-volume-control/sound-theme-file-utils.h new file mode 100644 index 0000000..0ac248b --- /dev/null +++ b/mate-volume-control/sound-theme-file-utils.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * Copyright (C) 2008 Bastien Nocera + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __SOUND_THEME_FILE_UTILS_HH__ +#define __SOUND_THEME_FILE_UTILS_HH__ + +#include + +char *custom_theme_dir_path (const char *child); +gboolean custom_theme_dir_is_empty (void); +void create_custom_theme (const char *parent); + +void delete_custom_theme_dir (void); +void delete_old_files (const char **sounds); +void delete_disabled_files (const char **sounds); + +void add_disabled_file (const char **sounds); +void add_custom_file (const char **sounds, const char *filename); + +void custom_theme_update_time (void); + +#endif /* __SOUND_THEME_FILE_UTILS_HH__ */ diff --git a/mate-volume-control/src/Makefile.am b/mate-volume-control/src/Makefile.am deleted file mode 100644 index 38f63ea..0000000 --- a/mate-volume-control/src/Makefile.am +++ /dev/null @@ -1,73 +0,0 @@ -NULL = - -bin_PROGRAMS = \ - mate-volume-control-applet \ - mate-volume-control \ - $(NULL) - -AM_CPPFLAGS = \ - $(WARN_CFLAGS) \ - -I$(top_srcdir)/sound-theme \ - $(VOLUME_CONTROL_CFLAGS) \ - $(DISABLE_DEPRECATED) \ - -DLOCALE_DIR=\""$(datadir)/locale"\" \ - -DLIBEXECDIR=\"$(libexecdir)\" \ - -DGLADEDIR=\""$(pkgdatadir)"\" \ - -DICON_DATA_DIR="\"$(pkgdatadir)/icons\"" \ - $(NULL) - -noinst_LTLIBRARIES = libmatevolumecontrol.la -libmatevolumecontrol_la_SOURCES = \ - gvc-channel-bar.h \ - gvc-channel-bar.c \ - gvc-utils.c \ - gvc-utils.h \ - $(NULL) - -mate_volume_control_applet_LDADD = \ - -lm \ - libmatevolumecontrol.la \ - $(VOLUME_CONTROL_LIBS) \ - $(NULL) - -mate_volume_control_applet_SOURCES = \ - gvc-stream-status-icon.h \ - gvc-stream-status-icon.c \ - gvc-applet.h \ - gvc-applet.c \ - applet-main.c \ - $(NULL) - -mate_volume_control_LDADD = \ - -lm \ - libmatevolumecontrol.la \ - $(top_builddir)/sound-theme/libsoundtheme.la \ - $(VOLUME_CONTROL_LIBS) \ - $(NULL) - -mate_volume_control_SOURCES = \ - gvc-balance-bar.h \ - gvc-balance-bar.c \ - gvc-level-bar.h \ - gvc-level-bar.c \ - gvc-combo-box.h \ - gvc-combo-box.c \ - gvc-speaker-test.h \ - gvc-speaker-test.c \ - gvc-mixer-dialog.h \ - gvc-mixer-dialog.c \ - dialog-main.c \ - $(NULL) - -BUILT_SOURCES = \ - $(NULL) - -CLEANFILES = \ - $(BUILT_SOURCES) \ - $(NULL) - -MAINTAINERCLEANFILES = \ - *~ \ - Makefile.in - --include $(top_srcdir)/git.mk diff --git a/mate-volume-control/src/applet-main.c b/mate-volume-control/src/applet-main.c deleted file mode 100644 index 1b4bb0a..0000000 --- a/mate-volume-control/src/applet-main.c +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "gvc-applet.h" - -static gboolean show_version = FALSE; -static gboolean debug = FALSE; - -int -main (int argc, char **argv) -{ - GError *error = NULL; - GvcApplet *applet; - UniqueApp *app; - GOptionEntry entries[] = { - { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, - { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, N_("Enable debug"), NULL }, - { NULL } - }; - - bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - - gtk_init_with_args (&argc, &argv, - _(" — MATE Volume Control Applet"), - entries, GETTEXT_PACKAGE, - &error); - - if (error != NULL) { - g_warning ("%s", error->message); - return 1; - } - if (show_version == TRUE) { - g_print ("%s %s\n", argv[0], VERSION); - return 0; - } - if (debug == TRUE) { - g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); - } - - app = unique_app_new (GVC_APPLET_DBUS_NAME, NULL); - - if (unique_app_is_running (app) == TRUE) { - g_warning ("Applet is already running, exiting"); - return 0; - } - if (mate_mixer_init () == FALSE) { - g_warning ("libmatemixer initialization failed, exiting"); - return 1; - } - - gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), - ICON_DATA_DIR); - - applet = gvc_applet_new (); - - gvc_applet_start (applet); - gtk_main (); - - g_object_unref (applet); - g_object_unref (app); - - return 0; -} diff --git a/mate-volume-control/src/dialog-main.c b/mate-volume-control/src/dialog-main.c deleted file mode 100644 index 08c55fc..0000000 --- a/mate-volume-control/src/dialog-main.c +++ /dev/null @@ -1,274 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include "config.h" - -#include -#include -#include - -#include -#include -#include - -#include "gvc-mixer-dialog.h" - -#define DIALOG_POPUP_TIMEOUT 3 - -static guint popup_id = 0; -static gboolean debug = FALSE; -static gboolean show_version = FALSE; - -static gchar *page = NULL; -static GtkWidget *app_dialog = NULL; -static GtkWidget *warning_dialog = NULL; - -static void -on_dialog_response (GtkDialog *dialog, guint response_id, gpointer data) -{ - gboolean destroy = GPOINTER_TO_INT (data); - - if (destroy == TRUE) - gtk_widget_destroy (GTK_WIDGET (dialog)); - - gtk_main_quit (); -} - -static void -on_dialog_close (GtkDialog *dialog, gpointer data) -{ - gboolean destroy = GPOINTER_TO_INT (data); - - if (destroy == TRUE) - gtk_widget_destroy (GTK_WIDGET (dialog)); - - gtk_main_quit (); -} - -static UniqueResponse -on_app_message_received (UniqueApp *app, - int command, - UniqueMessageData *message_data, - guint time_, - gpointer user_data) -{ - gtk_window_present (GTK_WINDOW (user_data)); - - return UNIQUE_RESPONSE_OK; -} - -static void -remove_warning_dialog (void) -{ - if (popup_id != 0) { - g_source_remove (popup_id); - popup_id = 0; - } - - g_clear_pointer (&warning_dialog, gtk_widget_destroy); -} - -static void -context_ready (MateMixerContext *context, UniqueApp *app) -{ - /* The dialog might be already created, e.g. when reconnected - * to a sound server */ - if (app_dialog != NULL) - return; - - app_dialog = GTK_WIDGET (gvc_mixer_dialog_new (context)); - - g_signal_connect (G_OBJECT (app_dialog), - "response", - G_CALLBACK (on_dialog_response), - GINT_TO_POINTER (FALSE)); - g_signal_connect (G_OBJECT (app_dialog), - "close", - G_CALLBACK (on_dialog_close), - GINT_TO_POINTER (FALSE)); - - gvc_mixer_dialog_set_page (GVC_MIXER_DIALOG (app_dialog), page); - - g_signal_connect (G_OBJECT (app), - "message-received", - G_CALLBACK (on_app_message_received), - app_dialog); - - gtk_widget_show (app_dialog); -} - -static void -on_context_state_notify (MateMixerContext *context, - GParamSpec *pspec, - UniqueApp *app) -{ - MateMixerState state = mate_mixer_context_get_state (context); - - if (state == MATE_MIXER_STATE_READY) { - remove_warning_dialog (); - context_ready (context, app); - } - else if (state == MATE_MIXER_STATE_FAILED) { - GtkWidget *dialog; - - remove_warning_dialog (); - - dialog = gtk_message_dialog_new (GTK_WINDOW (app_dialog), - 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("Sound system is not available")); - - g_signal_connect (G_OBJECT (dialog), - "response", - G_CALLBACK (on_dialog_response), - GINT_TO_POINTER (TRUE)); - g_signal_connect (G_OBJECT (dialog), - "close", - G_CALLBACK (on_dialog_close), - GINT_TO_POINTER (TRUE)); - - gtk_widget_show (dialog); - } -} - -static gboolean -dialog_popup_timeout (gpointer data) -{ - warning_dialog = gtk_message_dialog_new (GTK_WINDOW (app_dialog), - 0, - GTK_MESSAGE_INFO, - GTK_BUTTONS_CANCEL, - _("Waiting for sound system to respond")); - - g_signal_connect (G_OBJECT (warning_dialog), - "response", - G_CALLBACK (on_dialog_response), - GINT_TO_POINTER (TRUE)); - g_signal_connect (G_OBJECT (warning_dialog), - "close", - G_CALLBACK (on_dialog_close), - GINT_TO_POINTER (TRUE)); - - gtk_widget_show (warning_dialog); - - return FALSE; -} - -int -main (int argc, char **argv) -{ - GError *error = NULL; - gchar *backend = NULL; - MateMixerContext *context; - UniqueApp *app; - GOptionEntry entries[] = { - { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, N_("Sound system backend"), "pulse|alsa|oss|null" }, - { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, N_("Enable debug"), NULL }, - { "page", 'p', 0, G_OPTION_ARG_STRING, &page, N_("Startup page"), "effects|hardware|input|output|applications" }, - { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, - { NULL } - }; - - bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - - gtk_init_with_args (&argc, &argv, - _(" — MATE Volume Control"), - entries, GETTEXT_PACKAGE, - &error); - - if (error != NULL) { - g_warning ("%s", error->message); - return 1; - } - if (show_version == TRUE) { - g_print ("%s %s\n", argv[0], VERSION); - return 0; - } - if (debug == TRUE) { - g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); - } - - app = unique_app_new (GVC_DIALOG_DBUS_NAME, NULL); - - if (unique_app_is_running (app) == TRUE) { - unique_app_send_message (app, UNIQUE_ACTIVATE, NULL); - return 0; - } - if (mate_mixer_init () == FALSE) { - g_warning ("libmatemixer initialization failed, exiting"); - return 1; - } - - context = mate_mixer_context_new (); - - if (backend != NULL) { - if (strcmp (backend, "pulse") == 0) - mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_PULSEAUDIO); - else if (strcmp (backend, "alsa") == 0) - mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_ALSA); - else if (strcmp (backend, "oss") == 0) - mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_OSS); - else if (strcmp (backend, "null") == 0) - mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_NULL); - else { - g_warning ("Invalid backend: %s", backend); - g_object_unref (context); - g_object_unref (app); - g_free (backend); - return 1; - } - - g_free (backend); - } - - mate_mixer_context_set_app_name (context, _("Volume Control")); - mate_mixer_context_set_app_id (context, GVC_DIALOG_DBUS_NAME); - mate_mixer_context_set_app_version (context, VERSION); - mate_mixer_context_set_app_icon (context, "multimedia-volume-control"); - - g_signal_connect (G_OBJECT (context), - "notify::state", - G_CALLBACK (on_context_state_notify), - app); - - mate_mixer_context_open (context); - - if (mate_mixer_context_get_state (context) == MATE_MIXER_STATE_CONNECTING) { - popup_id = g_timeout_add_seconds (DIALOG_POPUP_TIMEOUT, - dialog_popup_timeout, - NULL); - } - - gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), - ICON_DATA_DIR); - - gtk_window_set_default_icon_name ("multimedia-volume-control"); - - gtk_main (); - - g_object_unref (context); - g_object_unref (app); - - return 0; -} diff --git a/mate-volume-control/src/gvc-applet.c b/mate-volume-control/src/gvc-applet.c deleted file mode 100644 index 3cb691f..0000000 --- a/mate-volume-control/src/gvc-applet.c +++ /dev/null @@ -1,357 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include - -#include "gvc-applet.h" -#include "gvc-stream-status-icon.h" - -#define GVC_APPLET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_APPLET, GvcAppletPrivate)) - -static const gchar *icon_names_output[] = { - "audio-volume-muted", - "audio-volume-low", - "audio-volume-medium", - "audio-volume-high", - NULL -}; - -static const gchar *icon_names_input[] = { - "audio-input-microphone-muted", - "audio-input-microphone-low", - "audio-input-microphone-medium", - "audio-input-microphone-high", - NULL -}; - -struct _GvcAppletPrivate -{ - GvcStreamStatusIcon *icon_input; - GvcStreamStatusIcon *icon_output; - gboolean running; - MateMixerContext *context; - MateMixerStream *input; -}; - -static void gvc_applet_class_init (GvcAppletClass *klass); -static void gvc_applet_init (GvcApplet *applet); - -G_DEFINE_TYPE (GvcApplet, gvc_applet, G_TYPE_OBJECT) - -static void -update_icon_input (GvcApplet *applet) -{ - MateMixerStreamControl *control = NULL; - gboolean show = FALSE; - - /* Enable the input icon in case there is an input stream present and there - * is a non-mixer application using the input */ - if (applet->priv->input != NULL) { - const gchar *app_id; - const GList *inputs = - mate_mixer_stream_list_controls (applet->priv->input); - - control = mate_mixer_stream_get_default_control (applet->priv->input); - - while (inputs != NULL) { - MateMixerStreamControl *input = - MATE_MIXER_STREAM_CONTROL (inputs->data); - MateMixerStreamControlRole role = - mate_mixer_stream_control_get_role (input); - - if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) { - MateMixerAppInfo *app_info = - mate_mixer_stream_control_get_app_info (input); - - app_id = mate_mixer_app_info_get_id (app_info); - if (app_id == NULL) { - /* A recording application which has no - * identifier set */ - g_debug ("Found a recording application control %s", - mate_mixer_stream_control_get_label (input)); - - if G_UNLIKELY (control == NULL) { - /* In the unlikely case when there is no - * default input control, use the application - * control for the icon */ - control = input; - } - show = TRUE; - break; - } - - if (strcmp (app_id, "org.mate.VolumeControl") != 0 && - strcmp (app_id, "org.gnome.VolumeControl") != 0 && - strcmp (app_id, "org.PulseAudio.pavucontrol") != 0) { - g_debug ("Found a recording application %s", app_id); - - if G_UNLIKELY (control == NULL) - control = input; - - show = TRUE; - break; - } - } - inputs = inputs->next; - } - - if (show == TRUE) - g_debug ("Input icon enabled"); - else - g_debug ("There is no recording application, input icon disabled"); - } - - gvc_stream_status_icon_set_control (applet->priv->icon_input, control); - - gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_input), show); -} - -static void -update_icon_output (GvcApplet *applet) -{ - MateMixerStream *stream; - MateMixerStreamControl *control = NULL; - - stream = mate_mixer_context_get_default_output_stream (applet->priv->context); - if (stream != NULL) - control = mate_mixer_stream_get_default_control (stream); - - gvc_stream_status_icon_set_control (applet->priv->icon_output, control); - - if (control != NULL) { - g_debug ("Output icon enabled"); - gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), - TRUE); - } - else { - g_debug ("There is no output stream/control, output icon disabled"); - gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), - FALSE); - } -} - -static void -on_input_stream_control_added (MateMixerStream *stream, - const gchar *name, - GvcApplet *applet) -{ - MateMixerStreamControl *control; - - control = mate_mixer_stream_get_control (stream, name); - if G_LIKELY (control != NULL) { - MateMixerStreamControlRole role = - mate_mixer_stream_control_get_role (control); - - /* Non-application input control doesn't affect the icon */ - if (role != MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) - return; - } - - /* Either an application control has been added or we couldn't - * read the control, this shouldn't happen but let's revalidate the - * icon to be sure if it does */ - update_icon_input (applet); -} - -static void -on_input_stream_control_removed (MateMixerStream *stream, - const gchar *name, - GvcApplet *applet) -{ - /* The removed stream could be an application input, which may cause - * the input status icon to disappear */ - update_icon_input (applet); -} - -static gboolean -update_default_input_stream (GvcApplet *applet) -{ - MateMixerStream *stream; - - stream = mate_mixer_context_get_default_input_stream (applet->priv->context); - if (stream == applet->priv->input) - return FALSE; - - /* The input stream has changed */ - if (applet->priv->input != NULL) { - g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input), - applet); - g_object_unref (applet->priv->input); - } - - applet->priv->input = g_object_ref (stream); - if (applet->priv->input != NULL) { - g_signal_connect (G_OBJECT (applet->priv->input), - "control-added", - G_CALLBACK (on_input_stream_control_added), - applet); - g_signal_connect (G_OBJECT (applet->priv->input), - "control-removed", - G_CALLBACK (on_input_stream_control_removed), - applet); - } - - /* Return TRUE if the default input stream has changed */ - return TRUE; -} - -static void -on_context_state_notify (MateMixerContext *context, - GParamSpec *pspec, - GvcApplet *applet) -{ - MateMixerState state = mate_mixer_context_get_state (context); - - switch (state) { - case MATE_MIXER_STATE_FAILED: - g_warning ("Failed to connect to a sound system"); - break; - - case MATE_MIXER_STATE_READY: - update_default_input_stream (applet); - - /* Each status change may affect the visibility of the icons */ - update_icon_output (applet); - update_icon_input (applet); - break; - default: - break; - } -} - -static void -on_context_default_input_stream_notify (MateMixerContext *context, - GParamSpec *pspec, - GvcApplet *applet) -{ - if (update_default_input_stream (applet) == FALSE) - return; - - update_icon_input (applet); -} - -static void -on_context_default_output_stream_notify (MateMixerContext *control, - GParamSpec *pspec, - GvcApplet *applet) -{ - update_icon_output (applet); -} - -void -gvc_applet_start (GvcApplet *applet) -{ - g_return_if_fail (GVC_IS_APPLET (applet)); - - if G_UNLIKELY (applet->priv->running == TRUE) - return; - - if G_UNLIKELY (mate_mixer_context_open (applet->priv->context) == FALSE) { - /* Normally this should never happen, in the worst case we - * should end up with the Null module */ - g_warning ("Failed to connect to a sound system"); - } - - g_debug ("Applet has been started"); - - applet->priv->running = TRUE; -} - -static void -gvc_applet_dispose (GObject *object) -{ - GvcApplet *applet = GVC_APPLET (object); - - if (applet->priv->input != NULL) { - g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input), - applet); - g_clear_object (&applet->priv->input); - } - - g_clear_object (&applet->priv->context); - g_clear_object (&applet->priv->icon_input); - g_clear_object (&applet->priv->icon_output); - - G_OBJECT_CLASS (gvc_applet_parent_class)->dispose (object); -} - -static void -gvc_applet_class_init (GvcAppletClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gvc_applet_dispose; - - g_type_class_add_private (klass, sizeof (GvcAppletPrivate)); -} - -static void -gvc_applet_init (GvcApplet *applet) -{ - applet->priv = GVC_APPLET_GET_PRIVATE (applet); - - applet->priv->icon_input = gvc_stream_status_icon_new (NULL, icon_names_input); - applet->priv->icon_output = gvc_stream_status_icon_new (NULL, icon_names_output); - - gvc_stream_status_icon_set_display_name (applet->priv->icon_input, _("Input")); - gvc_stream_status_icon_set_display_name (applet->priv->icon_output, _("Output")); - - gtk_status_icon_set_title (GTK_STATUS_ICON (applet->priv->icon_input), - _("Microphone Volume")); - gtk_status_icon_set_title (GTK_STATUS_ICON (applet->priv->icon_output), - _("Sound Output Volume")); - - applet->priv->context = mate_mixer_context_new (); - - mate_mixer_context_set_app_name (applet->priv->context, - _("MATE Volume Control Applet")); - - mate_mixer_context_set_app_id (applet->priv->context, GVC_APPLET_DBUS_NAME); - mate_mixer_context_set_app_version (applet->priv->context, VERSION); - mate_mixer_context_set_app_icon (applet->priv->context, "multimedia-volume-control"); - - g_signal_connect (G_OBJECT (applet->priv->context), - "notify::state", - G_CALLBACK (on_context_state_notify), - applet); - g_signal_connect (G_OBJECT (applet->priv->context), - "notify::default-input-stream", - G_CALLBACK (on_context_default_input_stream_notify), - applet); - g_signal_connect (G_OBJECT (applet->priv->context), - "notify::default-output-stream", - G_CALLBACK (on_context_default_output_stream_notify), - applet); -} - -GvcApplet * -gvc_applet_new (void) -{ - return g_object_new (GVC_TYPE_APPLET, NULL); -} diff --git a/mate-volume-control/src/gvc-applet.h b/mate-volume-control/src/gvc-applet.h deleted file mode 100644 index 991ef6d..0000000 --- a/mate-volume-control/src/gvc-applet.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_APPLET_H -#define __GVC_APPLET_H - -#include -#include - -G_BEGIN_DECLS - -#define GVC_APPLET_DBUS_NAME "org.mate.VolumeControlApplet" - -#define GVC_TYPE_APPLET (gvc_applet_get_type ()) -#define GVC_APPLET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_APPLET, GvcApplet)) -#define GVC_APPLET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GVC_TYPE_APPLET, GvcAppletClass)) -#define GVC_IS_APPLET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_APPLET)) -#define GVC_IS_APPLET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_APPLET)) -#define GVC_APPLET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_APPLET, GvcAppletClass)) - -typedef struct _GvcApplet GvcApplet; -typedef struct _GvcAppletClass GvcAppletClass; -typedef struct _GvcAppletPrivate GvcAppletPrivate; - -struct _GvcApplet -{ - GObject parent; - GvcAppletPrivate *priv; -}; - -struct _GvcAppletClass -{ - GObjectClass parent_class; -}; - -GType gvc_applet_get_type (void) G_GNUC_CONST; - -GvcApplet * gvc_applet_new (void); -void gvc_applet_start (GvcApplet *applet); - -G_END_DECLS - -#endif /* __GVC_APPLET_H */ diff --git a/mate-volume-control/src/gvc-balance-bar.c b/mate-volume-control/src/gvc-balance-bar.c deleted file mode 100644 index 7431773..0000000 --- a/mate-volume-control/src/gvc-balance-bar.c +++ /dev/null @@ -1,552 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 William Jon McCann - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include - -#include -#include - -#include "gvc-balance-bar.h" - -#define BALANCE_BAR_STYLE \ - "style \"balance-bar-scale-style\" {\n" \ - " GtkScale::trough-side-details = 0\n" \ - "}\n" \ - "widget \"*.balance-bar-scale\" style : rc \"balance-bar-scale-style\"\n" - -#define SCALE_SIZE 128 -#define GVC_BALANCE_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBarPrivate)) - -struct _GvcBalanceBarPrivate -{ - GvcBalanceType btype; - GtkWidget *scale_box; - GtkWidget *start_box; - GtkWidget *end_box; - GtkWidget *label; - GtkWidget *scale; - GtkAdjustment *adjustment; - GtkSizeGroup *size_group; - gboolean symmetric; - MateMixerStreamControl *control; - gint lfe_channel; -}; - -enum -{ - PROP_0, - PROP_CONTROL, - PROP_BALANCE_TYPE, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void gvc_balance_bar_class_init (GvcBalanceBarClass *klass); -static void gvc_balance_bar_init (GvcBalanceBar *balance_bar); -static void gvc_balance_bar_dispose (GObject *object); - -static gboolean on_scale_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcBalanceBar *bar); - -static void on_adjustment_value_changed (GtkAdjustment *adjustment, - GvcBalanceBar *bar); - -#if GTK_CHECK_VERSION (3, 0, 0) -G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_BOX) -#else -G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_HBOX) -#endif - -static void -create_scale_box (GvcBalanceBar *bar) -{ -#if GTK_CHECK_VERSION (3, 0, 0) - bar->priv->scale_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - bar->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - bar->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - bar->priv->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, - bar->priv->adjustment); -#if GTK_CHECK_VERSION (3, 4, 0) - /* Balance and fade scales do not have an origin */ - if (bar->priv->btype != BALANCE_TYPE_LFE) - gtk_scale_set_has_origin (GTK_SCALE (bar->priv->scale), FALSE); -#endif -#else - bar->priv->scale_box = gtk_hbox_new (FALSE, 6); - bar->priv->start_box = gtk_hbox_new (FALSE, 6); - bar->priv->end_box = gtk_hbox_new (FALSE, 6); - bar->priv->scale = gtk_hscale_new (bar->priv->adjustment); - - /* GTK2 way to remove the origin */ - if (bar->priv->btype != BALANCE_TYPE_LFE) { - gtk_rc_parse_string (BALANCE_BAR_STYLE); - gtk_widget_set_name (bar->priv->scale, "balance-bar-scale"); - } -#endif - - gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1); - - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->start_box, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->start_box), - bar->priv->label, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->scale, - TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->end_box, - FALSE, FALSE, 0); - - ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE); - - gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK); - - g_signal_connect (G_OBJECT (bar->priv->scale), - "scroll-event", - G_CALLBACK (on_scale_scroll_event), - bar); - - if (bar->priv->size_group != NULL) { - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->start_box); - - if (bar->priv->symmetric) - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->end_box); - } - - gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE); -} - -static void -update_scale_marks (GvcBalanceBar *bar) -{ - gchar *str_lower = NULL, - *str_upper = NULL; - gdouble lower, - upper; - - gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale)); - - switch (bar->priv->btype) { - case BALANCE_TYPE_RL: - str_lower = g_strdup_printf ("%s", C_("balance", "Left")); - str_upper = g_strdup_printf ("%s", C_("balance", "Right")); - break; - case BALANCE_TYPE_FR: - str_lower = g_strdup_printf ("%s", C_("balance", "Rear")); - str_upper = g_strdup_printf ("%s", C_("balance", "Front")); - break; - case BALANCE_TYPE_LFE: - str_lower = g_strdup_printf ("%s", C_("balance", "Minimum")); - str_upper = g_strdup_printf ("%s", C_("balance", "Maximum")); - break; - } - - lower = gtk_adjustment_get_lower (bar->priv->adjustment); - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), - lower, - GTK_POS_BOTTOM, - str_lower); - upper = gtk_adjustment_get_upper (bar->priv->adjustment); - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), - upper, - GTK_POS_BOTTOM, - str_upper); - g_free (str_lower); - g_free (str_upper); - - if (bar->priv->btype != BALANCE_TYPE_LFE) - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), - (upper - lower) / 2 + lower, - GTK_POS_BOTTOM, - NULL); -} - -void -gvc_balance_bar_set_size_group (GvcBalanceBar *bar, - GtkSizeGroup *group, - gboolean symmetric) -{ - g_return_if_fail (GVC_IS_BALANCE_BAR (bar)); - g_return_if_fail (GTK_IS_SIZE_GROUP (group)); - - bar->priv->size_group = group; - bar->priv->symmetric = symmetric; - - if (bar->priv->size_group != NULL) { - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->start_box); - - if (bar->priv->symmetric) - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->end_box); - } - gtk_widget_queue_draw (GTK_WIDGET (bar)); -} - -static void -update_balance_value (GvcBalanceBar *bar) -{ - gdouble value = 0; - - switch (bar->priv->btype) { - case BALANCE_TYPE_RL: - value = mate_mixer_stream_control_get_balance (bar->priv->control); - g_debug ("Balance value changed to %.2f", value); - break; - case BALANCE_TYPE_FR: - value = mate_mixer_stream_control_get_fade (bar->priv->control); - g_debug ("Fade value changed to %.2f", value); - break; - case BALANCE_TYPE_LFE: - value = mate_mixer_stream_control_get_channel_volume (bar->priv->control, - bar->priv->lfe_channel); - - g_debug ("Subwoofer volume changed to %.0f", value); - break; - } - - gtk_adjustment_set_value (bar->priv->adjustment, value); -} - -static void -on_balance_value_changed (MateMixerStream *stream, - GParamSpec *pspec, - GvcBalanceBar *bar) -{ - update_balance_value (bar); -} - -static gint -find_stream_lfe_channel (MateMixerStreamControl *control) -{ - guint i; - - for (i = 0; i < mate_mixer_stream_control_get_num_channels (control); i++) { - MateMixerChannelPosition position; - - position = mate_mixer_stream_control_get_channel_position (control, i); - if (position == MATE_MIXER_CHANNEL_LFE) - return i; - } - - return -1; -} - -static void -gvc_balance_bar_set_control (GvcBalanceBar *bar, MateMixerStreamControl *control) -{ - g_return_if_fail (GVC_BALANCE_BAR (bar)); - g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control)); - - if (bar->priv->control != NULL) { - g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), - on_balance_value_changed, - bar); - g_object_unref (bar->priv->control); - } - - bar->priv->control = g_object_ref (control); - - if (bar->priv->btype == BALANCE_TYPE_LFE) { - gdouble minimum; - gdouble maximum; - - minimum = mate_mixer_stream_control_get_min_volume (bar->priv->control); - maximum = mate_mixer_stream_control_get_normal_volume (bar->priv->control); - - /* Configure the adjustment for the volume limits of the current - * stream. - * Only subwoofer scale uses volume, balance and fade use fixed - * limits which do not need to be updated as balance type is - * only set during construction. */ - gtk_adjustment_configure (GTK_ADJUSTMENT (bar->priv->adjustment), - gtk_adjustment_get_value (bar->priv->adjustment), - minimum, - maximum, - (maximum - minimum) / 100.0, - (maximum - minimum) / 10.0, - 0.0); - - bar->priv->lfe_channel = find_stream_lfe_channel (bar->priv->control); - - if (G_LIKELY (bar->priv->lfe_channel > -1)) - g_debug ("Found LFE channel at position %d", bar->priv->lfe_channel); - else - g_warn_if_reached (); - } else - bar->priv->lfe_channel = -1; - - switch (bar->priv->btype) { - case BALANCE_TYPE_RL: - g_signal_connect (G_OBJECT (bar->priv->control), - "notify::balance", - G_CALLBACK (on_balance_value_changed), - bar); - break; - case BALANCE_TYPE_FR: - g_signal_connect (G_OBJECT (bar->priv->control), - "notify::fade", - G_CALLBACK (on_balance_value_changed), - bar); - break; - case BALANCE_TYPE_LFE: - g_signal_connect (G_OBJECT (bar->priv->control), - "notify::volume", - G_CALLBACK (on_balance_value_changed), - bar); - break; - } - - update_balance_value (bar); - update_scale_marks (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_CONTROL]); -} - -static void -gvc_balance_bar_set_balance_type (GvcBalanceBar *bar, GvcBalanceType btype) -{ - GtkWidget *frame; - GtkAdjustment *adjustment; - - /* Create adjustment with limits for balance and fade types because - * some limits must be provided. - * If subwoofer type is used instead, the limits will be changed when - * stream is set. */ - adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -1.0, 1.0, 0.05, 0.5, 0.0)); - - bar->priv->btype = btype; - bar->priv->adjustment = GTK_ADJUSTMENT (g_object_ref_sink (adjustment)); - - g_signal_connect (G_OBJECT (adjustment), - "value-changed", - G_CALLBACK (on_adjustment_value_changed), - bar); - - switch (btype) { - case BALANCE_TYPE_RL: - bar->priv->label = gtk_label_new_with_mnemonic (_("_Balance:")); - break; - case BALANCE_TYPE_FR: - bar->priv->label = gtk_label_new_with_mnemonic (_("_Fade:")); - break; - case BALANCE_TYPE_LFE: - bar->priv->label = gtk_label_new_with_mnemonic (_("_Subwoofer:")); - break; - } - - gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.0); - - /* Frame */ - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0); - - /* Box with scale */ - create_scale_box (bar); - gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); - - gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label), - bar->priv->scale); - - gtk_widget_set_direction (bar->priv->scale, GTK_TEXT_DIR_LTR); - gtk_widget_show_all (frame); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_BALANCE_TYPE]); -} - -static void -gvc_balance_bar_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GvcBalanceBar *self = GVC_BALANCE_BAR (object); - - switch (prop_id) { - case PROP_CONTROL: - gvc_balance_bar_set_control (self, g_value_get_object (value)); - break; - case PROP_BALANCE_TYPE: - gvc_balance_bar_set_balance_type (self, g_value_get_int (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_balance_bar_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GvcBalanceBar *self = GVC_BALANCE_BAR (object); - - switch (prop_id) { - case PROP_CONTROL: - g_value_set_object (value, self->priv->control); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_balance_bar_class_init (GvcBalanceBarClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gvc_balance_bar_dispose; - object_class->set_property = gvc_balance_bar_set_property; - object_class->get_property = gvc_balance_bar_get_property; - - properties[PROP_CONTROL] = - g_param_spec_object ("control", - "Control", - "MateMixer stream control", - MATE_MIXER_TYPE_STREAM_CONTROL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_BALANCE_TYPE] = - g_param_spec_int ("balance-type", - "balance type", - "Whether the balance is right-left or front-rear", - BALANCE_TYPE_RL, - NUM_BALANCE_TYPES - 1, - BALANCE_TYPE_RL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (klass, sizeof (GvcBalanceBarPrivate)); -} - -static gboolean -on_scale_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcBalanceBar *bar) -{ - gdouble value; - gdouble minimum; - gdouble maximum; - gdouble step; - - value = gtk_adjustment_get_value (bar->priv->adjustment); - minimum = gtk_adjustment_get_lower (bar->priv->adjustment); - maximum = gtk_adjustment_get_upper (bar->priv->adjustment); - - // XXX fix this for GTK3 - - if (bar->priv->btype == BALANCE_TYPE_LFE) - step = (maximum - minimum) / 100.0; - else - step = 0.05; - - if (event->direction == GDK_SCROLL_UP) { - if (value + step > maximum) - value = maximum; - else - value = value + step; - } else if (event->direction == GDK_SCROLL_DOWN) { - if (value - step < minimum) - value = minimum; - else - value = value - step; - } - - gtk_adjustment_set_value (bar->priv->adjustment, value); - return TRUE; -} - -static void -on_adjustment_value_changed (GtkAdjustment *adjustment, GvcBalanceBar *bar) -{ - gdouble value; - - if (bar->priv->control == NULL) - return; - - value = gtk_adjustment_get_value (adjustment); - - switch (bar->priv->btype) { - case BALANCE_TYPE_RL: - mate_mixer_stream_control_set_balance (bar->priv->control, value); - break; - case BALANCE_TYPE_FR: - mate_mixer_stream_control_set_fade (bar->priv->control, value); - break; - case BALANCE_TYPE_LFE: - mate_mixer_stream_control_set_channel_volume (bar->priv->control, - bar->priv->lfe_channel, - value); - break; - } -} - -static void -gvc_balance_bar_init (GvcBalanceBar *bar) -{ - bar->priv = GVC_BALANCE_BAR_GET_PRIVATE (bar); -} - -static void -gvc_balance_bar_dispose (GObject *object) -{ - GvcBalanceBar *bar; - - bar = GVC_BALANCE_BAR (object); - - if (bar->priv->control != NULL) { - g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), - on_balance_value_changed, - bar); - g_clear_object (&bar->priv->control); - } - - G_OBJECT_CLASS (gvc_balance_bar_parent_class)->dispose (object); -} - -GtkWidget * -gvc_balance_bar_new (MateMixerStreamControl *control, GvcBalanceType btype) -{ - return g_object_new (GVC_TYPE_BALANCE_BAR, - "balance-type", btype, - "control", control, -#if GTK_CHECK_VERSION (3, 0, 0) - "orientation", GTK_ORIENTATION_HORIZONTAL, -#endif - NULL); -} diff --git a/mate-volume-control/src/gvc-balance-bar.h b/mate-volume-control/src/gvc-balance-bar.h deleted file mode 100644 index d5b142d..0000000 --- a/mate-volume-control/src/gvc-balance-bar.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_BALANCE_BAR_H -#define __GVC_BALANCE_BAR_H - -#include -#include -#include - -#include - -G_BEGIN_DECLS - -#define GVC_TYPE_BALANCE_BAR (gvc_balance_bar_get_type ()) -#define GVC_BALANCE_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBar)) -#define GVC_BALANCE_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_BALANCE_BAR, GvcBalanceBarClass)) -#define GVC_IS_BALANCE_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_BALANCE_BAR)) -#define GVC_IS_BALANCE_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_BALANCE_BAR)) -#define GVC_BALANCE_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBarClass)) - -typedef enum { - BALANCE_TYPE_RL, - BALANCE_TYPE_FR, - BALANCE_TYPE_LFE, -} GvcBalanceType; - -#define NUM_BALANCE_TYPES BALANCE_TYPE_LFE + 1 - -typedef struct _GvcBalanceBar GvcBalanceBar; -typedef struct _GvcBalanceBarClass GvcBalanceBarClass; -typedef struct _GvcBalanceBarPrivate GvcBalanceBarPrivate; - -struct _GvcBalanceBar -{ -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBox parent; -#else - GtkHBox parent; -#endif - GvcBalanceBarPrivate *priv; -}; - -struct _GvcBalanceBarClass -{ -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBoxClass parent_class; -#else - GtkHBoxClass parent_class; -#endif -}; - -GType gvc_balance_bar_get_type (void) G_GNUC_CONST; - -GtkWidget * gvc_balance_bar_new (MateMixerStreamControl *control, - GvcBalanceType btype); - -void gvc_balance_bar_set_size_group (GvcBalanceBar *bar, - GtkSizeGroup *group, - gboolean symmetric); - -G_END_DECLS - -#endif /* __GVC_BALANCE_BAR_H */ diff --git a/mate-volume-control/src/gvc-channel-bar.c b/mate-volume-control/src/gvc-channel-bar.c deleted file mode 100644 index e431651..0000000 --- a/mate-volume-control/src/gvc-channel-bar.c +++ /dev/null @@ -1,1158 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 William Jon McCann - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include - -#include "gvc-channel-bar.h" - -#define SCALE_SIZE 128 -#define GVC_CHANNEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarPrivate)) - -struct _GvcChannelBarPrivate -{ - GtkOrientation orientation; - GtkWidget *scale_box; - GtkWidget *start_box; - GtkWidget *end_box; - GtkWidget *image; - GtkWidget *label; - GtkWidget *low_image; - GtkWidget *scale; - GtkWidget *high_image; - GtkWidget *mute_box; - GtkWidget *mute_button; - GtkAdjustment *adjustment; - gboolean show_icons; - gboolean show_mute; - gboolean show_marks; - gboolean extended; - GtkSizeGroup *size_group; - gboolean symmetric; - gboolean click_lock; - MateMixerStreamControl *control; - MateMixerStreamControlFlags control_flags; -}; - -enum { - PROP_0, - PROP_CONTROL, - PROP_ORIENTATION, - PROP_SHOW_ICONS, - PROP_SHOW_MUTE, - PROP_SHOW_MARKS, - PROP_EXTENDED, - PROP_NAME, - PROP_ICON_NAME, - PROP_LOW_ICON_NAME, - PROP_HIGH_ICON_NAME, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void gvc_channel_bar_class_init (GvcChannelBarClass *klass); -static void gvc_channel_bar_init (GvcChannelBar *bar); - -static gboolean on_scale_button_press_event (GtkWidget *widget, - GdkEventButton *event, - GvcChannelBar *bar); -static gboolean on_scale_button_release_event (GtkWidget *widget, - GdkEventButton *event, - GvcChannelBar *bar); -static gboolean on_scale_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcChannelBar *bar); - -#if GTK_CHECK_VERSION (3, 0, 0) -G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_BOX) -#else -G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_HBOX) -#endif - -static void -create_scale_box (GvcChannelBar *bar) -{ -#if GTK_CHECK_VERSION (3, 0, 0) - bar->priv->scale_box = gtk_box_new (bar->priv->orientation, 6); - bar->priv->start_box = gtk_box_new (bar->priv->orientation, 6); - bar->priv->end_box = gtk_box_new (bar->priv->orientation, 6); - bar->priv->scale = gtk_scale_new (bar->priv->orientation, - bar->priv->adjustment); -#else - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - bar->priv->scale_box = gtk_vbox_new (FALSE, 6); - bar->priv->start_box = gtk_vbox_new (FALSE, 6); - bar->priv->end_box = gtk_vbox_new (FALSE, 6); - bar->priv->scale = gtk_vscale_new (bar->priv->adjustment); - } else { - bar->priv->scale_box = gtk_hbox_new (FALSE, 6); - bar->priv->start_box = gtk_hbox_new (FALSE, 6); - bar->priv->end_box = gtk_hbox_new (FALSE, 6); - bar->priv->scale = gtk_hscale_new (bar->priv->adjustment); - } -#endif - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - gtk_widget_set_size_request (bar->priv->scale, -1, SCALE_SIZE); - - gtk_range_set_inverted (GTK_RANGE (bar->priv->scale), TRUE); - - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->start_box, - FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (bar->priv->start_box), - bar->priv->image, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->start_box), - bar->priv->label, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->start_box), - bar->priv->high_image, - FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->scale, - TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->end_box, - FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (bar->priv->end_box), - bar->priv->low_image, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->end_box), - bar->priv->mute_box, - FALSE, FALSE, 0); - } else { - gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1); - - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->image, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->start_box, - FALSE, FALSE, 0); - - gtk_box_pack_end (GTK_BOX (bar->priv->start_box), - bar->priv->low_image, - FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (bar->priv->start_box), - bar->priv->label, - TRUE, TRUE, 0); - - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->scale, - TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), - bar->priv->end_box, - FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (bar->priv->end_box), - bar->priv->high_image, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (bar->priv->end_box), - bar->priv->mute_box, - FALSE, FALSE, 0); - } - - if (bar->priv->show_icons) { - gtk_widget_show (bar->priv->low_image); - gtk_widget_show (bar->priv->high_image); - } else { - gtk_widget_hide (bar->priv->low_image); - gtk_widget_hide (bar->priv->high_image); - } - - ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE); - - gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK); - - g_signal_connect (G_OBJECT (bar->priv->scale), - "button-press-event", - G_CALLBACK (on_scale_button_press_event), - bar); - g_signal_connect (G_OBJECT (bar->priv->scale), - "button-release-event", - G_CALLBACK (on_scale_button_release_event), - bar); - g_signal_connect (G_OBJECT (bar->priv->scale), - "scroll-event", - G_CALLBACK (on_scale_scroll_event), - bar); - - if (bar->priv->size_group != NULL) { - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->start_box); - - if (bar->priv->symmetric) - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->end_box); - } - - gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE); -} - -static void -on_adjustment_value_changed (GtkAdjustment *adjustment, - GvcChannelBar *bar) -{ - gdouble value; - gdouble lower; - - if (bar->priv->control == NULL || bar->priv->click_lock == TRUE) - return; - - value = gtk_adjustment_get_value (bar->priv->adjustment); - lower = gtk_adjustment_get_lower (bar->priv->adjustment); - - if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE) - mate_mixer_stream_control_set_mute (bar->priv->control, (value <= lower)); - - if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE) - mate_mixer_stream_control_set_volume (bar->priv->control, (guint) value); -} - -static void -on_mute_button_toggled (GtkToggleButton *button, GvcChannelBar *bar) -{ - gboolean mute; - - mute = gtk_toggle_button_get_active (button); - - mate_mixer_stream_control_set_mute (bar->priv->control, mute); -} - -static void -update_layout (GvcChannelBar *bar) -{ - GtkWidget *frame; - - if (bar->priv->scale == NULL) - return; - - frame = gtk_widget_get_parent (bar->priv->scale_box); - - g_object_ref (bar->priv->image); - g_object_ref (bar->priv->label); - g_object_ref (bar->priv->mute_box); - g_object_ref (bar->priv->low_image); - g_object_ref (bar->priv->high_image); - - // XXX this is not the opposite of what is done above - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), - bar->priv->image); - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), - bar->priv->label); - gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), - bar->priv->mute_box); - - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), - bar->priv->low_image); - gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), - bar->priv->high_image); - } else { - gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), - bar->priv->low_image); - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), - bar->priv->high_image); - } - - gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), - bar->priv->start_box); - gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), - bar->priv->scale); - gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), - bar->priv->end_box); - gtk_container_remove (GTK_CONTAINER (frame), - bar->priv->scale_box); - - create_scale_box (bar); - gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); - - g_object_unref (bar->priv->image); - g_object_unref (bar->priv->label); - g_object_unref (bar->priv->mute_box); - g_object_unref (bar->priv->low_image); - g_object_unref (bar->priv->high_image); - - gtk_widget_show_all (frame); -} - -static void -update_marks (GvcChannelBar *bar) -{ - gdouble base; - gdouble normal; - gboolean has_mark = FALSE; - - gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale)); - - if (bar->priv->control == NULL || bar->priv->show_marks == FALSE) - return; - - /* Base volume represents unamplified volume, normal volume is the 100% - * volume, in many cases they are the same as unamplified volume is unknown */ - base = mate_mixer_stream_control_get_base_volume (bar->priv->control); - normal = mate_mixer_stream_control_get_normal_volume (bar->priv->control); - - if (normal <= gtk_adjustment_get_lower (bar->priv->adjustment)) - return; - - if (base < normal) { - gchar *str = g_strdup_printf ("%s", C_("volume", "Unamplified")); - - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), - base, - GTK_POS_BOTTOM, - str); - has_mark = TRUE; - g_free (str); - } - - /* Only show 100% mark if the scale is extended beyond 100% and - * there is no unamplified mark or it is below the normal volume */ - if (bar->priv->extended && (base == normal || base < normal)) { - gchar *str = g_strdup_printf ("%s", C_("volume", "100%")); - - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), - normal, - GTK_POS_BOTTOM, - str); - has_mark = TRUE; - g_free (str); - } - - if (has_mark) { - gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0, 0, 0); - - gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0); - gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0); - gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0); - } else { - gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0.5, 0, 0); - - gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0.5); - gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0.5); - gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0.5); - } -} - -static void -update_adjustment_value (GvcChannelBar *bar) -{ - gdouble value; - gboolean set_lower = FALSE; - - /* Move the slider to the minimal value if the stream control is muted or - * volume is unavailable */ - if (bar->priv->control == NULL) - set_lower = TRUE; - else if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) - set_lower = mate_mixer_stream_control_get_mute (bar->priv->control); - - if (set_lower == TRUE) - value = gtk_adjustment_get_lower (bar->priv->adjustment); - else - value = mate_mixer_stream_control_get_volume (bar->priv->control); - - g_signal_handlers_block_by_func (G_OBJECT (bar->priv->adjustment), - on_adjustment_value_changed, - bar); - - gtk_adjustment_set_value (bar->priv->adjustment, value); - - g_signal_handlers_unblock_by_func (G_OBJECT (bar->priv->adjustment), - on_adjustment_value_changed, - bar); -} - -static void -update_adjustment_limits (GvcChannelBar *bar) -{ - gdouble minimum = 0.0; - gdouble maximum = 0.0; - - if (bar->priv->control != NULL) { - minimum = mate_mixer_stream_control_get_min_volume (bar->priv->control); - if (bar->priv->extended) - maximum = mate_mixer_stream_control_get_max_volume (bar->priv->control); - else - maximum = mate_mixer_stream_control_get_normal_volume (bar->priv->control); - } - - gtk_adjustment_configure (bar->priv->adjustment, - gtk_adjustment_get_value (bar->priv->adjustment), - minimum, - maximum, - (maximum - minimum) / 100.0, - (maximum - minimum) / 15.0, - 0.0); -} - -static void -update_mute_button (GvcChannelBar *bar) -{ - if (bar->priv->show_mute == TRUE) { - gboolean enable = FALSE; - - if (bar->priv->control != NULL && - bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) - enable = TRUE; - - if (enable == TRUE) { - gboolean mute = mate_mixer_stream_control_get_mute (bar->priv->control); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), - mute); - } else { - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), - FALSE); - } - - gtk_widget_set_sensitive (bar->priv->mute_button, enable); - gtk_widget_show (bar->priv->mute_button); - } else - gtk_widget_hide (bar->priv->mute_button); -} - -static gboolean -on_scale_button_press_event (GtkWidget *widget, - GdkEventButton *event, - GvcChannelBar *bar) -{ - -#if !GTK_CHECK_VERSION (3, 6, 0) - /* Up to GTK 3.4 the slider selection only moves in increments when - * clicking in the slider with the left button and it moves directly - * to the clicked position with the middle mouse button. - * Change this behaviour to also jump to the clicked position with the - * left mouse button. */ - if (event->button == 1) - event->button = 2; -#endif - - /* Muting the stream when volume is non-zero moves the slider to zero, - * but the volume remains the same. In this case delay unmuting and - * changing volume until user releases the mouse button. */ - if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE && - bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { - if (mate_mixer_stream_control_get_mute (bar->priv->control) == TRUE) { - guint minimum = (guint) gtk_adjustment_get_lower (bar->priv->adjustment); - - if (mate_mixer_stream_control_get_volume (bar->priv->control) > minimum) - bar->priv->click_lock = TRUE; - } - } - return FALSE; -} - -static gboolean -on_scale_button_release_event (GtkWidget *widget, - GdkEventButton *event, - GvcChannelBar *bar) -{ -#if !GTK_CHECK_VERSION (3, 6, 0) - if (event->button == 1) - event->button = 2; -#endif - - if (bar->priv->click_lock == TRUE) { - /* The volume change is not reflected while the lock is - * held, propagate the change now that user has released - * the mouse button */ - bar->priv->click_lock = FALSE; - on_adjustment_value_changed (bar->priv->adjustment, bar); - } - - /* Play a sound */ - ca_gtk_play_for_widget (GTK_WIDGET (bar), 0, - CA_PROP_EVENT_ID, "audio-volume-change", - CA_PROP_EVENT_DESCRIPTION, "Volume change", - CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", - CA_PROP_APPLICATION_NAME, _("Volume Control"), - CA_PROP_APPLICATION_VERSION, VERSION, - CA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control", - NULL); - return FALSE; -} - -static gboolean -on_scale_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcChannelBar *bar) -{ - GdkScrollDirection direction = event->direction; - -#if GTK_CHECK_VERSION (3, 4, 0) - if (direction == GDK_SCROLL_SMOOTH) { - gdouble dx = 0.0; - gdouble dy = 0.0; - - gdk_event_get_scroll_deltas ((const GdkEvent *) event, &dx, &dy); - if (dy > 0.0) - direction = GDK_SCROLL_DOWN; - else if (dy < 0.0) - direction = GDK_SCROLL_UP; - else - return FALSE; - } -#endif - return gvc_channel_bar_scroll (bar, direction); -} - -static void -on_control_volume_notify (MateMixerStreamControl *control, - GParamSpec *pspec, - GvcChannelBar *bar) -{ - update_adjustment_value (bar); -} - -static void -on_control_mute_notify (MateMixerStreamControl *control, - GParamSpec *pspec, - GvcChannelBar *bar) -{ - if (bar->priv->show_mute == TRUE) { - gboolean mute = mate_mixer_stream_control_get_mute (control); - - g_signal_handlers_block_by_func (G_OBJECT (bar->priv->mute_button), - on_mute_button_toggled, - bar); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), mute); - - g_signal_handlers_unblock_by_func (G_OBJECT (bar->priv->mute_button), - on_mute_button_toggled, - bar); - } - update_adjustment_value (bar); -} - -MateMixerStreamControl * -gvc_channel_bar_get_control (GvcChannelBar *bar) -{ - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); - - return bar->priv->control; -} - -void -gvc_channel_bar_set_control (GvcChannelBar *bar, MateMixerStreamControl *control) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - if (bar->priv->control == control) - return; - - if (control != NULL) - g_object_ref (control); - - if (bar->priv->control != NULL) { - g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), - G_CALLBACK (on_control_volume_notify), - bar); - g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->control), - G_CALLBACK (on_control_mute_notify), - bar); - g_object_unref (bar->priv->control); - } - - bar->priv->control = control; - - if (control != NULL) - bar->priv->control_flags = mate_mixer_stream_control_get_flags (control); - else - bar->priv->control_flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS; - - if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) - g_signal_connect (G_OBJECT (control), - "notify::volume", - G_CALLBACK (on_control_volume_notify), - bar); - if (bar->priv->control_flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) - g_signal_connect (G_OBJECT (control), - "notify::mute", - G_CALLBACK (on_control_mute_notify), - bar); - - update_marks (bar); - update_mute_button (bar); - update_adjustment_limits (bar); - update_adjustment_value (bar); -} - -GtkOrientation -gvc_channel_bar_get_orientation (GvcChannelBar *bar) -{ - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), 0); - - return bar->priv->orientation; -} - -void -gvc_channel_bar_set_orientation (GvcChannelBar *bar, - GtkOrientation orientation) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - if (orientation == bar->priv->orientation) - return; - - bar->priv->orientation = orientation; - update_layout (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]); -} - -gboolean -gvc_channel_bar_get_show_icons (GvcChannelBar *bar) -{ - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); - - return bar->priv->show_icons; -} - -void -gvc_channel_bar_set_show_icons (GvcChannelBar *bar, gboolean show_icons) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - if (show_icons == bar->priv->show_icons) - return; - - bar->priv->show_icons = show_icons; - - if (bar->priv->show_icons == TRUE) { - gtk_widget_show (bar->priv->low_image); - gtk_widget_show (bar->priv->high_image); - } else { - gtk_widget_hide (bar->priv->low_image); - gtk_widget_hide (bar->priv->high_image); - } - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_ICONS]); -} - -gboolean -gvc_channel_bar_get_show_mute (GvcChannelBar *bar) -{ - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); - - return bar->priv->show_mute; -} - -void -gvc_channel_bar_set_show_mute (GvcChannelBar *bar, gboolean show_mute) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - if (show_mute == bar->priv->show_mute) - return; - - bar->priv->show_mute = show_mute; - update_mute_button (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MUTE]); -} - -gboolean -gvc_channel_bar_get_show_marks (GvcChannelBar *bar) -{ - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); - - return bar->priv->show_marks; -} - -void -gvc_channel_bar_set_show_marks (GvcChannelBar *bar, gboolean show_marks) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - if (show_marks == bar->priv->show_marks) - return; - - bar->priv->show_marks = show_marks; - update_marks (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MARKS]); -} - -gboolean -gvc_channel_bar_get_extended (GvcChannelBar *bar) -{ - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); - - return bar->priv->extended; -} - -void -gvc_channel_bar_set_extended (GvcChannelBar *bar, gboolean extended) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - if (extended == bar->priv->extended) - return; - - bar->priv->extended = extended; - - /* Update displayed marks as non-extended scales do not show the 100% - * limit at the end of the scale */ - update_marks (bar); - update_adjustment_limits (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_EXTENDED]); -} - -const gchar * -gvc_channel_bar_get_name (GvcChannelBar *bar) -{ - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); - - return gtk_label_get_text (GTK_LABEL (bar->priv->label)); -} - -void -gvc_channel_bar_set_name (GvcChannelBar *bar, const gchar *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - if (name != NULL) { - gtk_label_set_text_with_mnemonic (GTK_LABEL (bar->priv->label), name); - gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label), - bar->priv->scale); - - gtk_widget_show (bar->priv->label); - } else { - gtk_label_set_text (GTK_LABEL (bar->priv->label), NULL); - gtk_widget_hide (bar->priv->label); - } - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_NAME]); -} - -const gchar * -gvc_channel_bar_get_icon_name (GvcChannelBar *bar) -{ - const gchar *name = NULL; - - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); - - gtk_image_get_icon_name (GTK_IMAGE (bar->priv->image), &name, NULL); - return name; -} - -void -gvc_channel_bar_set_icon_name (GvcChannelBar *bar, const gchar *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->image), - name, - GTK_ICON_SIZE_DIALOG); - if (name != NULL) - gtk_widget_show (bar->priv->image); - else - gtk_widget_hide (bar->priv->image); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ICON_NAME]); -} - -const gchar * -gvc_channel_bar_get_low_icon_name (GvcChannelBar *bar) -{ - const gchar *name = NULL; - - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); - - gtk_image_get_icon_name (GTK_IMAGE (bar->priv->low_image), &name, NULL); - return name; -} - -void -gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, const gchar *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->low_image), - name, - GTK_ICON_SIZE_BUTTON); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_LOW_ICON_NAME]); -} - -const gchar * -gvc_channel_bar_get_high_icon_name (GvcChannelBar *bar) -{ - const gchar *name = NULL; - - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); - - gtk_image_get_icon_name (GTK_IMAGE (bar->priv->high_image), &name, NULL); - return name; -} - -void -gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, const gchar *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - - gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->high_image), - name, - GTK_ICON_SIZE_BUTTON); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_HIGH_ICON_NAME]); -} - -gboolean -gvc_channel_bar_scroll (GvcChannelBar *bar, GdkScrollDirection direction) -{ - gdouble value; - gdouble minimum; - gdouble maximum; - gdouble scrollstep; - - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); - - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - if (direction != GDK_SCROLL_UP && direction != GDK_SCROLL_DOWN) - return FALSE; - } else { - /* Switch direction for RTL */ - if (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_RTL) { - if (direction == GDK_SCROLL_RIGHT) - direction = GDK_SCROLL_LEFT; - else if (direction == GDK_SCROLL_LEFT) - direction = GDK_SCROLL_RIGHT; - } - - /* Switch side scroll to vertical */ - if (direction == GDK_SCROLL_RIGHT) - direction = GDK_SCROLL_UP; - else if (direction == GDK_SCROLL_LEFT) - direction = GDK_SCROLL_DOWN; - } - - value = gtk_adjustment_get_value (bar->priv->adjustment); - minimum = gtk_adjustment_get_lower (bar->priv->adjustment); - maximum = gtk_adjustment_get_upper (bar->priv->adjustment); - - scrollstep = maximum / 100.0 * 5.0; - - if (direction == GDK_SCROLL_UP) { - if (value + scrollstep > maximum) - value = maximum; - else - value = value + scrollstep; - } else if (direction == GDK_SCROLL_DOWN) { - if (value - scrollstep < minimum) - value = minimum; - else - value = value - scrollstep; - } - - gtk_adjustment_set_value (bar->priv->adjustment, value); - return TRUE; -} - -void -gvc_channel_bar_set_size_group (GvcChannelBar *bar, - GtkSizeGroup *group, - gboolean symmetric) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - g_return_if_fail (GTK_IS_SIZE_GROUP (group)); - - bar->priv->size_group = group; - bar->priv->symmetric = symmetric; - - if (bar->priv->size_group != NULL) { - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->start_box); - - if (bar->priv->symmetric) - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->end_box); - } - gtk_widget_queue_draw (GTK_WIDGET (bar)); -} - -static void -gvc_channel_bar_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GvcChannelBar *self = GVC_CHANNEL_BAR (object); - - switch (prop_id) { - case PROP_CONTROL: - gvc_channel_bar_set_control (self, g_value_get_object (value)); - break; - case PROP_ORIENTATION: - gvc_channel_bar_set_orientation (self, g_value_get_enum (value)); - break; - case PROP_SHOW_MUTE: - gvc_channel_bar_set_show_mute (self, g_value_get_boolean (value)); - break; - case PROP_SHOW_ICONS: - gvc_channel_bar_set_show_icons (self, g_value_get_boolean (value)); - break; - case PROP_SHOW_MARKS: - gvc_channel_bar_set_show_marks (self, g_value_get_boolean (value)); - break; - case PROP_EXTENDED: - gvc_channel_bar_set_extended (self, g_value_get_boolean (value)); - break; - case PROP_NAME: - gvc_channel_bar_set_name (self, g_value_get_string (value)); - break; - case PROP_ICON_NAME: - gvc_channel_bar_set_icon_name (self, g_value_get_string (value)); - break; - case PROP_LOW_ICON_NAME: - gvc_channel_bar_set_low_icon_name (self, g_value_get_string (value)); - break; - case PROP_HIGH_ICON_NAME: - gvc_channel_bar_set_high_icon_name (self, g_value_get_string (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_channel_bar_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GvcChannelBar *self = GVC_CHANNEL_BAR (object); - - switch (prop_id) { - case PROP_CONTROL: - g_value_set_object (value, self->priv->control); - break; - case PROP_ORIENTATION: - g_value_set_enum (value, self->priv->orientation); - break; - case PROP_SHOW_MUTE: - g_value_set_boolean (value, self->priv->show_mute); - break; - case PROP_SHOW_ICONS: - g_value_set_boolean (value, self->priv->show_icons); - break; - case PROP_SHOW_MARKS: - g_value_set_boolean (value, self->priv->show_marks); - break; - case PROP_EXTENDED: - g_value_set_boolean (value, self->priv->extended); - break; - case PROP_NAME: - g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label))); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_channel_bar_class_init (GvcChannelBarClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = gvc_channel_bar_set_property; - object_class->get_property = gvc_channel_bar_get_property; - - properties[PROP_CONTROL] = - g_param_spec_object ("control", - "Control", - "MateMixer stream control", - MATE_MIXER_TYPE_STREAM_CONTROL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_ORIENTATION] = - g_param_spec_enum ("orientation", - "Orientation", - "The orientation of the scale", - GTK_TYPE_ORIENTATION, - GTK_ORIENTATION_VERTICAL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_SHOW_MUTE] = - g_param_spec_boolean ("show-mute", - "show mute", - "Whether stream is muted", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_SHOW_ICONS] = - g_param_spec_boolean ("show-icons", - "show mute", - "Whether to show low and high icons", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_SHOW_MARKS] = - g_param_spec_boolean ("show-marks", - "Show marks", - "Whether to show scale marks", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_EXTENDED] = - g_param_spec_boolean ("extended", - "Extended", - "Allow the scale to be extended above normal volume", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_NAME] = - g_param_spec_string ("name", - "Name", - "Name to display for this stream", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_ICON_NAME] = - g_param_spec_string ("icon-name", - "Icon name", - "Name of icon to display for this stream", - NULL, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_LOW_ICON_NAME] = - g_param_spec_string ("low-icon-name", - "Low icon name", - "Name of low volume icon to display for this stream", - "audio-volume-low", - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_HIGH_ICON_NAME] = - g_param_spec_string ("high-icon-name", - "High icon name", - "Name of high volume icon to display for this stream", - "audio-volume-high", - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (klass, sizeof (GvcChannelBarPrivate)); -} - -static void -gvc_channel_bar_init (GvcChannelBar *bar) -{ - GtkWidget *frame; - - bar->priv = GVC_CHANNEL_BAR_GET_PRIVATE (bar); - - /* Mute button */ - bar->priv->mute_button = gtk_check_button_new_with_label (_("Mute")); - gtk_widget_set_no_show_all (bar->priv->mute_button, TRUE); - - g_signal_connect (bar->priv->mute_button, - "toggled", - G_CALLBACK (on_mute_button_toggled), - bar); - - bar->priv->mute_box = gtk_alignment_new (0.5, 0.5, 0, 0); - gtk_container_add (GTK_CONTAINER (bar->priv->mute_box), bar->priv->mute_button); - - bar->priv->image = gtk_image_new (); - gtk_widget_set_no_show_all (bar->priv->image, TRUE); - - /* Low/high icons */ - bar->priv->low_image = gtk_image_new (); - gtk_widget_set_no_show_all (bar->priv->low_image, TRUE); - - bar->priv->high_image = gtk_image_new (); - gtk_widget_set_no_show_all (bar->priv->high_image, TRUE); - - bar->priv->label = gtk_label_new (NULL); - gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.5); - gtk_widget_set_no_show_all (bar->priv->label, TRUE); - - /* Frame */ - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0); - - gtk_widget_show_all (frame); - - /* Create a default adjustment */ - bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 0, 0, 0, 0)); - - g_object_ref_sink (bar->priv->adjustment); - - g_signal_connect (bar->priv->adjustment, - "value-changed", - G_CALLBACK (on_adjustment_value_changed), - bar); - - /* Initially create a vertical scale box */ - bar->priv->orientation = GTK_ORIENTATION_VERTICAL; - - create_scale_box (bar); - - gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); -} - -GtkWidget * -gvc_channel_bar_new (MateMixerStreamControl *control) -{ - return g_object_new (GVC_TYPE_CHANNEL_BAR, - "control", control, -#if GTK_CHECK_VERSION (3, 0, 0) - "orientation", GTK_ORIENTATION_HORIZONTAL, -#endif - NULL); -} diff --git a/mate-volume-control/src/gvc-channel-bar.h b/mate-volume-control/src/gvc-channel-bar.h deleted file mode 100644 index 1c80be9..0000000 --- a/mate-volume-control/src/gvc-channel-bar.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_CHANNEL_BAR_H -#define __GVC_CHANNEL_BAR_H - -#include -#include -#include - -#include - -G_BEGIN_DECLS - -#define GVC_TYPE_CHANNEL_BAR (gvc_channel_bar_get_type ()) -#define GVC_CHANNEL_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBar)) -#define GVC_CHANNEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_CHANNEL_BAR, GvcChannelBarClass)) -#define GVC_IS_CHANNEL_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_CHANNEL_BAR)) -#define GVC_IS_CHANNEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_BAR)) -#define GVC_CHANNEL_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarClass)) - -typedef struct _GvcChannelBar GvcChannelBar; -typedef struct _GvcChannelBarClass GvcChannelBarClass; -typedef struct _GvcChannelBarPrivate GvcChannelBarPrivate; - -struct _GvcChannelBar -{ -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBox parent; -#else - GtkHBox parent; -#endif - GvcChannelBarPrivate *priv; -}; - -struct _GvcChannelBarClass -{ -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBoxClass parent_class; -#else - GtkHBoxClass parent_class; -#endif - - void (* changed) (GvcChannelBar *bar); -}; - -GType gvc_channel_bar_get_type (void); - -GtkWidget * gvc_channel_bar_new (MateMixerStreamControl *control); - -MateMixerStreamControl *gvc_channel_bar_get_control (GvcChannelBar *bar); -void gvc_channel_bar_set_control (GvcChannelBar *bar, - MateMixerStreamControl *control); - -const gchar * gvc_channel_bar_get_name (GvcChannelBar *bar); -void gvc_channel_bar_set_name (GvcChannelBar *bar, - const gchar *name); - -const gchar * gvc_channel_bar_get_icon_name (GvcChannelBar *bar); -void gvc_channel_bar_set_icon_name (GvcChannelBar *bar, - const gchar *icon_name); - -const gchar * gvc_channel_bar_get_low_icon_name (GvcChannelBar *bar); -void gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, - const gchar *icon_name); - -const gchar * gvc_channel_bar_get_high_icon_name (GvcChannelBar *bar); -void gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, - const gchar *icon_name); - -GtkOrientation gvc_channel_bar_get_orientation (GvcChannelBar *bar); -void gvc_channel_bar_set_orientation (GvcChannelBar *bar, - GtkOrientation orientation); - -gboolean gvc_channel_bar_get_show_icons (GvcChannelBar *bar); -void gvc_channel_bar_set_show_icons (GvcChannelBar *bar, - gboolean show_mute); - -gboolean gvc_channel_bar_get_show_mute (GvcChannelBar *bar); -void gvc_channel_bar_set_show_mute (GvcChannelBar *bar, - gboolean show_mute); - -gboolean gvc_channel_bar_get_show_marks (GvcChannelBar *bar); -void gvc_channel_bar_set_show_marks (GvcChannelBar *bar, - gboolean show_marks); - -gboolean gvc_channel_bar_get_extended (GvcChannelBar *bar); -void gvc_channel_bar_set_extended (GvcChannelBar *bar, - gboolean extended); - -void gvc_channel_bar_set_size_group (GvcChannelBar *bar, - GtkSizeGroup *group, - gboolean symmetric); - -gboolean gvc_channel_bar_scroll (GvcChannelBar *bar, - GdkScrollDirection direction); - -G_END_DECLS - -#endif /* __GVC_CHANNEL_BAR_H */ diff --git a/mate-volume-control/src/gvc-combo-box.c b/mate-volume-control/src/gvc-combo-box.c deleted file mode 100644 index b2a273f..0000000 --- a/mate-volume-control/src/gvc-combo-box.c +++ /dev/null @@ -1,397 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2009 Bastien Nocera - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include - -#include - -#include "gvc-combo-box.h" - -#define GVC_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_COMBO_BOX, GvcComboBoxPrivate)) - -struct _GvcComboBoxPrivate -{ - GtkWidget *drop_box; - GtkWidget *start_box; - GtkWidget *end_box; - GtkWidget *label; - GtkWidget *button; - GtkTreeModel *model; - GtkWidget *combobox; - gboolean set_called; - GtkSizeGroup *size_group; - gboolean symmetric; -}; - -enum { - COL_NAME, - COL_HUMAN_NAME, - NUM_COLS -}; - -enum { - CHANGED, - BUTTON_CLICKED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0, }; - -enum { - PROP_0, - PROP_LABEL, - PROP_SHOW_BUTTON, - PROP_BUTTON_LABEL, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void gvc_combo_box_class_init (GvcComboBoxClass *klass); -static void gvc_combo_box_init (GvcComboBox *combo); -static void gvc_combo_box_dispose (GObject *object); - -#if GTK_CHECK_VERSION (3, 0, 0) -G_DEFINE_TYPE (GvcComboBox, gvc_combo_box, GTK_TYPE_BOX) -#else -G_DEFINE_TYPE (GvcComboBox, gvc_combo_box, GTK_TYPE_HBOX) -#endif - -void -gvc_combo_box_set_size_group (GvcComboBox *combobox, - GtkSizeGroup *group, - gboolean symmetric) -{ - g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); - g_return_if_fail (GTK_IS_SIZE_GROUP (group)); - - combobox->priv->size_group = group; - combobox->priv->symmetric = symmetric; - - if (combobox->priv->size_group != NULL) { - gtk_size_group_add_widget (combobox->priv->size_group, - combobox->priv->start_box); - - if (combobox->priv->symmetric) - gtk_size_group_add_widget (combobox->priv->size_group, - combobox->priv->end_box); - } - gtk_widget_queue_draw (GTK_WIDGET (combobox)); -} - -static void -gvc_combo_box_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GvcComboBox *self = GVC_COMBO_BOX (object); - - switch (prop_id) { - case PROP_LABEL: - gtk_label_set_text_with_mnemonic (GTK_LABEL (self->priv->label), g_value_get_string (value)); - break; - case PROP_BUTTON_LABEL: - gtk_button_set_label (GTK_BUTTON (self->priv->button), g_value_get_string (value)); - break; - case PROP_SHOW_BUTTON: - gtk_widget_set_visible (self->priv->button, g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_combo_box_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GvcComboBox *self = GVC_COMBO_BOX (object); - - switch (prop_id) { - case PROP_LABEL: - g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label))); - break; - case PROP_BUTTON_LABEL: - g_value_set_string (value, gtk_button_get_label (GTK_BUTTON (self->priv->button))); - break; - case PROP_SHOW_BUTTON: - g_value_set_boolean (value, gtk_widget_get_visible (self->priv->button)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_combo_box_class_init (GvcComboBoxClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gvc_combo_box_dispose; - object_class->set_property = gvc_combo_box_set_property; - object_class->get_property = gvc_combo_box_get_property; - - properties[PROP_LABEL] = - g_param_spec_string ("label", - "label", - "The combo box label", - _("_Profile:"), - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - properties[PROP_SHOW_BUTTON] = - g_param_spec_boolean ("show-button", - "show-button", - "Whether to show the button", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - properties[PROP_BUTTON_LABEL] = - g_param_spec_string ("button-label", - "button-label", - "The button's label", - "", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - signals[CHANGED] = - g_signal_new ("changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GvcComboBoxClass, changed), - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, G_TYPE_STRING); - - signals[BUTTON_CLICKED] = - g_signal_new ("button-clicked", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GvcComboBoxClass, button_clicked), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0, G_TYPE_NONE); - - g_type_class_add_private (klass, sizeof (GvcComboBoxPrivate)); -} - -void -gvc_combo_box_set_options (GvcComboBox *combobox, const GList *options) -{ - const GList *l; - - g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); - g_return_if_fail (combobox->priv->set_called == FALSE); - - for (l = options; l != NULL; l = l->next) { - MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (l->data); - - gtk_list_store_insert_with_values (GTK_LIST_STORE (combobox->priv->model), - NULL, - G_MAXINT, - COL_NAME, - mate_mixer_switch_option_get_name (option), - COL_HUMAN_NAME, - mate_mixer_switch_option_get_label (option), - -1); - } - combobox->priv->set_called = TRUE; -} - -void -gvc_combo_box_set_active (GvcComboBox *combobox, const gchar *id) -{ - GtkTreeIter iter; - gboolean cont; - - g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); - g_return_if_fail (id != NULL); - - cont = gtk_tree_model_get_iter_first (combobox->priv->model, &iter); - while (cont != FALSE) { - gchar *name; - - gtk_tree_model_get (combobox->priv->model, &iter, - COL_NAME, &name, - -1); - if (g_strcmp0 (name, id) == 0) { - gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox->priv->combobox), &iter); - g_free (name); - return; - } - g_free (name); - - gtk_tree_model_iter_next (combobox->priv->model, &iter); - } - g_warning ("Could not find id '%s' in combo box", id); -} - -static void -on_combo_box_changed (GtkComboBox *widget, GvcComboBox *combobox) -{ - GtkTreeIter iter; - gchar *profile; - - if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter) == FALSE) { - g_warning ("Could not find an active profile or port"); - return; - } - - gtk_tree_model_get (combobox->priv->model, &iter, - COL_NAME, &profile, - -1); - - g_signal_emit (combobox, signals[CHANGED], 0, profile); - g_free (profile); -} - -static void -on_combo_box_button_clicked (GtkButton *button, GvcComboBox *combobox) -{ - g_signal_emit (combobox, signals[BUTTON_CLICKED], 0); -} - -static void -gvc_combo_box_init (GvcComboBox *combobox) -{ - GtkWidget *frame; - GtkCellRenderer *renderer; - - combobox->priv = GVC_COMBO_BOX_GET_PRIVATE (combobox); - - combobox->priv->model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS, - G_TYPE_STRING, - G_TYPE_STRING)); - combobox->priv->label = gtk_label_new (NULL); - - gtk_misc_set_alignment (GTK_MISC (combobox->priv->label), 0.0, 0.5); - - /* Frame */ - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (combobox), frame, TRUE, TRUE, 0); - -#if GTK_CHECK_VERSION (3, 0, 0) - combobox->priv->drop_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - combobox->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - combobox->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); -#else - combobox->priv->drop_box = gtk_hbox_new (FALSE, 6); - combobox->priv->start_box = gtk_hbox_new (FALSE, 6); - combobox->priv->end_box = gtk_hbox_new (FALSE, 6); -#endif - combobox->priv->combobox = gtk_combo_box_new_with_model (combobox->priv->model); - - renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox->priv->combobox), - renderer, - FALSE); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox->priv->combobox), - renderer, - "text", - COL_HUMAN_NAME); - -#if GTK_CHECK_VERSION (3, 0, 0) - /* Make sure the combo box does not get too long on long profile names */ - g_object_set (G_OBJECT (renderer), - "ellipsize", - PANGO_ELLIPSIZE_END, - NULL); - - gtk_combo_box_set_popup_fixed_width (GTK_COMBO_BOX (combobox->priv->combobox), FALSE); -#endif - - gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), - combobox->priv->start_box, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (combobox->priv->start_box), - combobox->priv->label, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), - combobox->priv->combobox, - TRUE, TRUE, 0); - - combobox->priv->button = gtk_button_new_with_label (""); - - gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), - combobox->priv->button, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), - combobox->priv->end_box, - FALSE, FALSE, 0); - - gtk_widget_set_no_show_all (combobox->priv->button, TRUE); - - if (combobox->priv->size_group != NULL) { - gtk_size_group_add_widget (combobox->priv->size_group, - combobox->priv->start_box); - - if (combobox->priv->symmetric) - gtk_size_group_add_widget (combobox->priv->size_group, - combobox->priv->end_box); - } - - gtk_label_set_mnemonic_widget (GTK_LABEL (combobox->priv->label), - combobox->priv->combobox); - - gtk_container_add (GTK_CONTAINER (frame), combobox->priv->drop_box); - gtk_widget_show_all (frame); - - g_signal_connect (G_OBJECT (combobox->priv->combobox), - "changed", - G_CALLBACK (on_combo_box_changed), - combobox); - g_signal_connect (G_OBJECT (combobox->priv->button), - "clicked", - G_CALLBACK (on_combo_box_button_clicked), - combobox); -} - -static void -gvc_combo_box_dispose (GObject *object) -{ - GvcComboBox *combobox; - - combobox = GVC_COMBO_BOX (object); - - g_clear_object (&combobox->priv->model); - - G_OBJECT_CLASS (gvc_combo_box_parent_class)->dispose (object); -} - -GtkWidget * -gvc_combo_box_new (const gchar *label) -{ - return g_object_new (GVC_TYPE_COMBO_BOX, - "label", label, -#if GTK_CHECK_VERSION (3, 0, 0) - "orientation", GTK_ORIENTATION_HORIZONTAL, -#endif - NULL); -} diff --git a/mate-volume-control/src/gvc-combo-box.h b/mate-volume-control/src/gvc-combo-box.h deleted file mode 100644 index 9a2f425..0000000 --- a/mate-volume-control/src/gvc-combo-box.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_COMBO_BOX_H -#define __GVC_COMBO_BOX_H - -#include -#include -#include - -G_BEGIN_DECLS - -#define GVC_TYPE_COMBO_BOX (gvc_combo_box_get_type ()) -#define GVC_COMBO_BOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_COMBO_BOX, GvcComboBox)) -#define GVC_COMBO_BOX_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_COMBO_BOX, GvcComboBoxClass)) -#define GVC_IS_COMBO_BOX(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_COMBO_BOX)) -#define GVC_IS_COMBO_BOX_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_COMBO_BOX)) -#define GVC_COMBO_BOX_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_COMBO_BOX, GvcComboBoxClass)) - -typedef struct _GvcComboBox GvcComboBox; -typedef struct _GvcComboBoxClass GvcComboBoxClass; -typedef struct _GvcComboBoxPrivate GvcComboBoxPrivate; - -struct _GvcComboBox -{ -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBox parent; -#else - GtkHBox parent; -#endif - GvcComboBoxPrivate *priv; -}; - -struct _GvcComboBoxClass -{ -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBoxClass parent_class; -#else - GtkHBoxClass parent_class; -#endif - void (* changed) (GvcComboBox *combobox, - const gchar *name); - void (* button_clicked) (GvcComboBox *combobox); -}; - -GType gvc_combo_box_get_type (void) G_GNUC_CONST; - -GtkWidget * gvc_combo_box_new (const gchar *label); - -void gvc_combo_box_set_size_group (GvcComboBox *combobox, - GtkSizeGroup *group, - gboolean symmetric); - -void gvc_combo_box_set_options (GvcComboBox *combobox, - const GList *options); -void gvc_combo_box_set_active (GvcComboBox *combobox, - const gchar *id); - -G_END_DECLS - -#endif /* __GVC_COMBO_BOX_H */ diff --git a/mate-volume-control/src/gvc-level-bar.c b/mate-volume-control/src/gvc-level-bar.c deleted file mode 100644 index 029b346..0000000 --- a/mate-volume-control/src/gvc-level-bar.c +++ /dev/null @@ -1,876 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 William Jon McCann - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -// XXX on gtk3 the last two squares don't get filled - -#include -#include -#include -#include -#include - -#include "gvc-level-bar.h" -#include "gvc-utils.h" - -#define GVC_LEVEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBarPrivate)) - -#define NUM_BOXES 15 -#define MIN_HORIZONTAL_BAR_WIDTH 150 -#define HORIZONTAL_BAR_HEIGHT 6 -#define VERTICAL_BAR_WIDTH 6 -#define MIN_VERTICAL_BAR_HEIGHT 400 - -typedef struct { - int peak_num; - int max_peak_num; - GdkRectangle area; - int delta; - int box_width; - int box_height; - int box_radius; -#if GTK_CHECK_VERSION (3, 0, 0) - GdkRGBA color_bg; - GdkRGBA color_fg; - GdkRGBA color_dark; -#else - GdkColor color_bg; - GdkColor color_fg; - GdkColor color_dark; -#endif -} LevelBarLayout; - -struct _GvcLevelBarPrivate -{ - GtkOrientation orientation; - GtkAdjustment *peak_adjustment; - GtkAdjustment *rms_adjustment; - GvcLevelScale scale; - gdouble peak_fraction; - gdouble rms_fraction; - gdouble max_peak; - guint max_peak_id; - LevelBarLayout layout; -}; - -enum -{ - PROP_0, - PROP_PEAK_ADJUSTMENT, - PROP_RMS_ADJUSTMENT, - PROP_SCALE, - PROP_ORIENTATION, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void gvc_level_bar_class_init (GvcLevelBarClass *klass); -static void gvc_level_bar_init (GvcLevelBar *bar); -static void gvc_level_bar_finalize (GObject *object); - -G_DEFINE_TYPE (GvcLevelBar, gvc_level_bar, GTK_TYPE_WIDGET) - -static gboolean -layout_changed (LevelBarLayout *layout1, LevelBarLayout *layout2) -{ - if (layout1->area.x != layout2->area.x) - return TRUE; - if (layout1->area.y != layout2->area.y) - return TRUE; - if (layout1->area.width != layout2->area.width) - return TRUE; - if (layout1->area.height != layout2->area.height) - return TRUE; - - if (layout1->delta != layout2->delta) - return TRUE; - if (layout1->peak_num != layout2->peak_num) - return TRUE; - if (layout1->max_peak_num != layout2->max_peak_num) - return TRUE; - -#if GTK_CHECK_VERSION (3, 0, 0) - if (!gdk_rgba_equal (&layout1->color_fg, &layout2->color_fg)) - return TRUE; - if (!gdk_rgba_equal (&layout1->color_bg, &layout2->color_bg)) - return TRUE; - if (!gdk_rgba_equal (&layout1->color_dark, &layout2->color_dark)) - return TRUE; -#else - if (!gdk_color_equal (&layout1->color_fg, &layout2->color_fg)) - return TRUE; - if (!gdk_color_equal (&layout1->color_bg, &layout2->color_bg)) - return TRUE; - if (!gdk_color_equal (&layout1->color_dark, &layout2->color_dark)) - return TRUE; -#endif - return FALSE; -} - -static gdouble -fraction_from_adjustment (GvcLevelBar *bar, - GtkAdjustment *adjustment) -{ - gdouble level; - gdouble fraction = 0.0; - gdouble min; - gdouble max; - - level = gtk_adjustment_get_value (adjustment); - min = gtk_adjustment_get_lower (adjustment); - max = gtk_adjustment_get_upper (adjustment); - - switch (bar->priv->scale) { - case GVC_LEVEL_SCALE_LINEAR: - fraction = (level - min) / (max - min); - break; - case GVC_LEVEL_SCALE_LOG: - fraction = log10 ((level - min + 1) / (max - min + 1)); - break; - } - - return fraction; -} - -static gboolean -reset_max_peak (GvcLevelBar *bar) -{ - bar->priv->max_peak = gtk_adjustment_get_lower (bar->priv->peak_adjustment); - - bar->priv->layout.max_peak_num = 0; - - gtk_widget_queue_draw (GTK_WIDGET (bar)); - - bar->priv->max_peak_id = 0; - return FALSE; -} - -static void -bar_calc_layout (GvcLevelBar *bar) -{ - int peak_level; - int max_peak_level; - GtkAllocation allocation; - -#if GTK_CHECK_VERSION (3, 0, 0) - GtkStyleContext *context; - - context = gtk_widget_get_style_context (GTK_WIDGET (bar)); - - gtk_style_context_get_background_color (context, - GTK_STATE_FLAG_NORMAL, - &bar->priv->layout.color_bg); - gtk_style_context_get_background_color (context, - GTK_STATE_FLAG_SELECTED, - &bar->priv->layout.color_fg); - gtk_style_context_get_color (context, - GTK_STATE_FLAG_NORMAL, - &bar->priv->layout.color_dark); - - gvc_color_shade (&bar->priv->layout.color_dark, - &bar->priv->layout.color_dark, - 0.7); -#else - GtkStyle *style; - - style = gtk_widget_get_style (GTK_WIDGET (bar)); - - bar->priv->layout.color_bg = style->bg[GTK_STATE_NORMAL]; - bar->priv->layout.color_fg = style->bg[GTK_STATE_SELECTED]; - bar->priv->layout.color_dark = style->dark[GTK_STATE_NORMAL]; -#endif - - gtk_widget_get_allocation (GTK_WIDGET (bar), &allocation); - - bar->priv->layout.area.width = allocation.width - 2; - bar->priv->layout.area.height = allocation.height - 2; - - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - peak_level = bar->priv->peak_fraction * bar->priv->layout.area.height; - max_peak_level = bar->priv->max_peak * bar->priv->layout.area.height; - - bar->priv->layout.delta = bar->priv->layout.area.height / NUM_BOXES; - bar->priv->layout.area.x = 0; - bar->priv->layout.area.y = 0; - bar->priv->layout.box_height = bar->priv->layout.delta / 2; - bar->priv->layout.box_width = bar->priv->layout.area.width; - bar->priv->layout.box_radius = bar->priv->layout.box_width / 2; - } else { - peak_level = bar->priv->peak_fraction * bar->priv->layout.area.width; - max_peak_level = bar->priv->max_peak * bar->priv->layout.area.width; - - bar->priv->layout.delta = bar->priv->layout.area.width / NUM_BOXES; - bar->priv->layout.area.x = 0; - bar->priv->layout.area.y = 0; - bar->priv->layout.box_width = bar->priv->layout.delta / 2; - bar->priv->layout.box_height = bar->priv->layout.area.height; - bar->priv->layout.box_radius = bar->priv->layout.box_height / 2; - } - - bar->priv->layout.peak_num = peak_level / bar->priv->layout.delta; - bar->priv->layout.max_peak_num = max_peak_level / bar->priv->layout.delta; -} - -static void -update_peak_value (GvcLevelBar *bar) -{ - gdouble value; - LevelBarLayout layout; - - value = fraction_from_adjustment (bar, bar->priv->peak_adjustment); - - bar->priv->peak_fraction = value; - - if (value > bar->priv->max_peak) { - if (bar->priv->max_peak_id > 0) - g_source_remove (bar->priv->max_peak_id); - - bar->priv->max_peak_id = - g_timeout_add_seconds (1, (GSourceFunc) reset_max_peak, bar); - bar->priv->max_peak = value; - } - - layout = bar->priv->layout; - - bar_calc_layout (bar); - - if (layout_changed (&bar->priv->layout, &layout)) - gtk_widget_queue_draw (GTK_WIDGET (bar)); -} - -static void -update_rms_value (GvcLevelBar *bar) -{ - bar->priv->rms_fraction = fraction_from_adjustment (bar, bar->priv->rms_adjustment); -} - -GtkOrientation -gvc_level_bar_get_orientation (GvcLevelBar *bar) -{ - g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), 0); - - return bar->priv->orientation; -} - -void -gvc_level_bar_set_orientation (GvcLevelBar *bar, - GtkOrientation orientation) -{ - g_return_if_fail (GVC_IS_LEVEL_BAR (bar)); - - if (orientation != bar->priv->orientation) { - if (G_UNLIKELY (orientation != GTK_ORIENTATION_VERTICAL && - orientation != GTK_ORIENTATION_HORIZONTAL)) { - g_warn_if_reached (); - return; - } - - bar->priv->orientation = orientation; - - gtk_widget_queue_draw (GTK_WIDGET (bar)); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]); - } -} - -static void -on_peak_adjustment_value_changed (GtkAdjustment *adjustment, - GvcLevelBar *bar) -{ - update_peak_value (bar); -} - -static void -on_rms_adjustment_value_changed (GtkAdjustment *adjustment, - GvcLevelBar *bar) -{ - update_rms_value (bar); -} - -void -gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar, - GtkAdjustment *adjustment) -{ - g_return_if_fail (GVC_LEVEL_BAR (bar)); - g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); - - if (bar->priv->peak_adjustment != NULL) { - g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->peak_adjustment), - G_CALLBACK (on_peak_adjustment_value_changed), - bar); - g_object_unref (bar->priv->peak_adjustment); - } - - bar->priv->peak_adjustment = g_object_ref_sink (adjustment); - - g_signal_connect (G_OBJECT (bar->priv->peak_adjustment), - "value-changed", - G_CALLBACK (on_peak_adjustment_value_changed), - bar); - - update_peak_value (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_PEAK_ADJUSTMENT]); -} - -void -gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar, - GtkAdjustment *adjustment) -{ - g_return_if_fail (GVC_LEVEL_BAR (bar)); - g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); - - if (bar->priv->rms_adjustment != NULL) { - g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->rms_adjustment), - G_CALLBACK (on_rms_adjustment_value_changed), - bar); - g_object_unref (bar->priv->rms_adjustment); - } - - bar->priv->rms_adjustment = g_object_ref_sink (adjustment); - - g_signal_connect (G_OBJECT (bar->priv->rms_adjustment), - "value-changed", - G_CALLBACK (on_rms_adjustment_value_changed), - bar); - - update_rms_value (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_RMS_ADJUSTMENT]); -} - -GtkAdjustment * -gvc_level_bar_get_peak_adjustment (GvcLevelBar *bar) -{ - g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), NULL); - - return bar->priv->peak_adjustment; -} - -GtkAdjustment * -gvc_level_bar_get_rms_adjustment (GvcLevelBar *bar) -{ - g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), NULL); - - return bar->priv->rms_adjustment; -} - -void -gvc_level_bar_set_scale (GvcLevelBar *bar, GvcLevelScale scale) -{ - g_return_if_fail (GVC_IS_LEVEL_BAR (bar)); - - if (scale != bar->priv->scale) { - if (G_UNLIKELY (scale != GVC_LEVEL_SCALE_LINEAR && - scale != GVC_LEVEL_SCALE_LOG)) { - g_warn_if_reached (); - return; - } - bar->priv->scale = scale; - - update_peak_value (bar); - update_rms_value (bar); - - g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SCALE]); - } -} - -static void -gvc_level_bar_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GvcLevelBar *self = GVC_LEVEL_BAR (object); - - switch (prop_id) { - case PROP_SCALE: - gvc_level_bar_set_scale (self, g_value_get_int (value)); - break; - case PROP_ORIENTATION: - gvc_level_bar_set_orientation (self, g_value_get_enum (value)); - break; - case PROP_PEAK_ADJUSTMENT: - gvc_level_bar_set_peak_adjustment (self, g_value_get_object (value)); - break; - case PROP_RMS_ADJUSTMENT: - gvc_level_bar_set_rms_adjustment (self, g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_level_bar_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GvcLevelBar *self = GVC_LEVEL_BAR (object); - - switch (prop_id) { - case PROP_SCALE: - g_value_set_int (value, self->priv->scale); - break; - case PROP_ORIENTATION: - g_value_set_enum (value, self->priv->orientation); - break; - case PROP_PEAK_ADJUSTMENT: - g_value_set_object (value, self->priv->peak_adjustment); - break; - case PROP_RMS_ADJUSTMENT: - g_value_set_object (value, self->priv->rms_adjustment); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_level_bar_size_request (GtkWidget *widget, - GtkRequisition *requisition) -{ - GvcLevelBar *bar; - - g_return_if_fail (GVC_IS_LEVEL_BAR (widget)); - g_return_if_fail (requisition != NULL); - - bar = GVC_LEVEL_BAR (widget); - - switch (bar->priv->orientation) { - case GTK_ORIENTATION_VERTICAL: - requisition->width = VERTICAL_BAR_WIDTH; - requisition->height = MIN_VERTICAL_BAR_HEIGHT; - break; - case GTK_ORIENTATION_HORIZONTAL: - requisition->width = MIN_HORIZONTAL_BAR_WIDTH; - requisition->height = HORIZONTAL_BAR_HEIGHT; - break; - } -} - -#if GTK_CHECK_VERSION (3, 0, 0) -static void -gvc_level_bar_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - GtkRequisition requisition; - - gvc_level_bar_size_request (widget, &requisition); - - if (minimum != NULL) - *minimum = requisition.width; - if (natural != NULL) - *natural = requisition.width; -} - -static void -gvc_level_bar_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - GtkRequisition requisition; - - gvc_level_bar_size_request (widget, &requisition); - - if (minimum != NULL) - *minimum = requisition.height; - if (natural != NULL) - *natural = requisition.height; -} -#endif - -static void -gvc_level_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation) -{ - GvcLevelBar *bar; - - bar = GVC_LEVEL_BAR (widget); - - /* FIXME: add height property, labels, etc */ - GTK_WIDGET_CLASS (gvc_level_bar_parent_class)->size_allocate (widget, allocation); - - gtk_widget_set_allocation (widget, allocation); - gtk_widget_get_allocation (widget, allocation); - - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - allocation->height = MIN (allocation->height, MIN_VERTICAL_BAR_HEIGHT); - allocation->width = MAX (allocation->width, VERTICAL_BAR_WIDTH); - } else { - allocation->width = MIN (allocation->width, MIN_HORIZONTAL_BAR_WIDTH); - allocation->height = MAX (allocation->height, HORIZONTAL_BAR_HEIGHT); - } - - bar_calc_layout (bar); -} - -static void -curved_rectangle (cairo_t *cr, - double x0, - double y0, - double width, - double height, - double radius) -{ - double x1; - double y1; - - x1 = x0 + width; - y1 = y0 + height; - - if (!width || !height) - return; - - if (width / 2 < radius) { - if (height / 2 < radius) { - cairo_move_to (cr, x0, (y0 + y1) / 2); - cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0); - cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); - cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); - cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); - } else { - cairo_move_to (cr, x0, y0 + radius); - cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0); - cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); - cairo_line_to (cr, x1, y1 - radius); - cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); - cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); - } - } else { - if (height / 2 < radius) { - cairo_move_to (cr, x0, (y0 + y1) / 2); - cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0); - cairo_line_to (cr, x1 - radius, y0); - cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); - cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); - cairo_line_to (cr, x0 + radius, y1); - cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); - } else { - cairo_move_to (cr, x0, y0 + radius); - cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0); - cairo_line_to (cr, x1 - radius, y0); - cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); - cairo_line_to (cr, x1, y1 - radius); - cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); - cairo_line_to (cr, x0 + radius, y1); - cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); - } - } - - cairo_close_path (cr); -} - -static int -gvc_level_bar_draw (GtkWidget *widget, cairo_t *cr) -{ - GvcLevelBar *bar; - - bar = GVC_LEVEL_BAR (widget); - - cairo_save (cr); - - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - int i; - int by; - - for (i = 0; i < NUM_BOXES; i++) { - by = i * bar->priv->layout.delta; - curved_rectangle (cr, - bar->priv->layout.area.x + 0.5, - by + 0.5, - bar->priv->layout.box_width - 1, - bar->priv->layout.box_height - 1, - bar->priv->layout.box_radius); - if ((bar->priv->layout.max_peak_num - 1) == i) { - /* fill peak foreground */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg); -#endif - cairo_fill_preserve (cr); - } else if ((bar->priv->layout.peak_num - 1) >= i) { - /* fill background */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); -#endif - cairo_fill_preserve (cr); - - /* fill foreground */ -#if GTK_CHECK_VERSION (3, 0, 0) - cairo_set_source_rgba (cr, - bar->priv->layout.color_fg.red, - bar->priv->layout.color_fg.green, - bar->priv->layout.color_fg.blue, - 0.5); -#else - cairo_set_source_rgba (cr, - bar->priv->layout.color_fg.red / 65535.0, - bar->priv->layout.color_fg.green / 65535.0, - bar->priv->layout.color_fg.blue / 65535.0, - 0.5); -#endif - cairo_fill_preserve (cr); - } else { - /* fill background */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); -#endif - cairo_fill_preserve (cr); - } - - /* stroke border */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark); -#endif - cairo_set_line_width (cr, 1); - cairo_stroke (cr); - } - } else { - int i; - int bx; - - if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) { - GtkAllocation allocation; - - gtk_widget_get_allocation (widget, &allocation); - - cairo_scale (cr, -1, 1); - cairo_translate (cr, -allocation.width, 0); - } - - for (i = 0; i < NUM_BOXES; i++) { - bx = i * bar->priv->layout.delta; - curved_rectangle (cr, - bx + 0.5, - bar->priv->layout.area.y + 0.5, - bar->priv->layout.box_width - 1, - bar->priv->layout.box_height - 1, - bar->priv->layout.box_radius); - - if ((bar->priv->layout.max_peak_num - 1) == i) { - /* fill peak foreground */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg); -#endif - cairo_fill_preserve (cr); - } else if ((bar->priv->layout.peak_num - 1) >= i) { - /* fill background */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); -#endif - cairo_fill_preserve (cr); - - /* fill foreground */ -#if GTK_CHECK_VERSION (3, 0, 0) - cairo_set_source_rgba (cr, - bar->priv->layout.color_fg.red, - bar->priv->layout.color_fg.green, - bar->priv->layout.color_fg.blue, - 0.5); -#else - cairo_set_source_rgba (cr, - bar->priv->layout.color_fg.red / 65535.0, - bar->priv->layout.color_fg.green / 65535.0, - bar->priv->layout.color_fg.blue / 65535.0, - 0.5); -#endif - cairo_fill_preserve (cr); - } else { - /* fill background */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); -#endif - cairo_fill_preserve (cr); - } - - /* stroke border */ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark); -#else - gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark); -#endif - cairo_set_line_width (cr, 1); - cairo_stroke (cr); - } - } - - cairo_restore (cr); - - return FALSE; -} - -#if !GTK_CHECK_VERSION (3, 0, 0) -static int -gvc_level_bar_expose (GtkWidget *widget, GdkEventExpose *event) -{ - cairo_t *cr; - GtkAllocation allocation; - - g_return_val_if_fail (event != NULL, FALSE); - - /* Event queue compression */ - if (event->count > 0) - return FALSE; - - gtk_widget_get_allocation (widget, &allocation); - - cr = gdk_cairo_create (gtk_widget_get_window (widget)); - cairo_translate (cr, - allocation.x, - allocation.y); - - gvc_level_bar_draw (widget, cr); - - cairo_destroy (cr); - return FALSE; -} -#endif - -static void -gvc_level_bar_class_init (GvcLevelBarClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = gvc_level_bar_finalize; - object_class->set_property = gvc_level_bar_set_property; - object_class->get_property = gvc_level_bar_get_property; - -#if GTK_CHECK_VERSION (3, 0, 0) - widget_class->draw = gvc_level_bar_draw; - widget_class->get_preferred_width = gvc_level_bar_get_preferred_width; - widget_class->get_preferred_height = gvc_level_bar_get_preferred_height; -#else - widget_class->expose_event = gvc_level_bar_expose; - widget_class->size_request = gvc_level_bar_size_request; -#endif - widget_class->size_allocate = gvc_level_bar_size_allocate; - - properties[PROP_ORIENTATION] = - g_param_spec_enum ("orientation", - "Orientation", - "The orientation of the bar", - GTK_TYPE_ORIENTATION, - GTK_ORIENTATION_HORIZONTAL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_PEAK_ADJUSTMENT] = - g_param_spec_object ("peak-adjustment", - "Peak Adjustment", - "The GtkAdjustment that contains the current peak value", - GTK_TYPE_ADJUSTMENT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_RMS_ADJUSTMENT] = - g_param_spec_object ("rms-adjustment", - "RMS Adjustment", - "The GtkAdjustment that contains the current rms value", - GTK_TYPE_ADJUSTMENT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_SCALE] = - g_param_spec_int ("scale", - "Scale", - "Scale", - 0, - G_MAXINT, - GVC_LEVEL_SCALE_LINEAR, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (klass, sizeof (GvcLevelBarPrivate)); -} - -static void -gvc_level_bar_init (GvcLevelBar *bar) -{ - bar->priv = GVC_LEVEL_BAR_GET_PRIVATE (bar); - - bar->priv->peak_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, - 0.0, - 1.0, - 0.05, - 0.1, - 0.1)); - g_object_ref_sink (bar->priv->peak_adjustment); - - g_signal_connect (bar->priv->peak_adjustment, - "value-changed", - G_CALLBACK (on_peak_adjustment_value_changed), - bar); - - bar->priv->rms_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, - 0.0, - 1.0, - 0.05, - 0.1, - 0.1)); - g_object_ref_sink (bar->priv->rms_adjustment); - - g_signal_connect (bar->priv->rms_adjustment, - "value-changed", - G_CALLBACK (on_rms_adjustment_value_changed), - bar); - - gtk_widget_set_has_window (GTK_WIDGET (bar), FALSE); -} - -static void -gvc_level_bar_finalize (GObject *object) -{ - GvcLevelBar *bar; - - bar = GVC_LEVEL_BAR (object); - - if (bar->priv->max_peak_id > 0) - g_source_remove (bar->priv->max_peak_id); - - G_OBJECT_CLASS (gvc_level_bar_parent_class)->finalize (object); -} - -GtkWidget * -gvc_level_bar_new (void) -{ - return g_object_new (GVC_TYPE_LEVEL_BAR, NULL); -} diff --git a/mate-volume-control/src/gvc-level-bar.h b/mate-volume-control/src/gvc-level-bar.h deleted file mode 100644 index ef9ae7e..0000000 --- a/mate-volume-control/src/gvc-level-bar.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 William Jon McCann - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_LEVEL_BAR_H -#define __GVC_LEVEL_BAR_H - -#include -#include -#include - -G_BEGIN_DECLS - -#define GVC_TYPE_LEVEL_BAR (gvc_level_bar_get_type ()) -#define GVC_LEVEL_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBar)) -#define GVC_LEVEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_LEVEL_BAR, GvcLevelBarClass)) -#define GVC_IS_LEVEL_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_LEVEL_BAR)) -#define GVC_IS_LEVEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_LEVEL_BAR)) -#define GVC_LEVEL_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBarClass)) - -typedef struct _GvcLevelBar GvcLevelBar; -typedef struct _GvcLevelBarClass GvcLevelBarClass; -typedef struct _GvcLevelBarPrivate GvcLevelBarPrivate; - -struct _GvcLevelBar -{ - GtkWidget parent; - GvcLevelBarPrivate *priv; -}; - -struct _GvcLevelBarClass -{ - GtkWidgetClass parent_class; -}; - -typedef enum -{ - GVC_LEVEL_SCALE_LINEAR, - GVC_LEVEL_SCALE_LOG -} GvcLevelScale; - -GType gvc_level_bar_get_type (void) G_GNUC_CONST; - -GtkWidget * gvc_level_bar_new (void); -void gvc_level_bar_set_orientation (GvcLevelBar *bar, - GtkOrientation orientation); -GtkOrientation gvc_level_bar_get_orientation (GvcLevelBar *bar); - -void gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar, - GtkAdjustment *adjustment); -GtkAdjustment * gvc_level_bar_get_peak_adjustment (GvcLevelBar *bar); - -void gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar, - GtkAdjustment *adjustment); -GtkAdjustment * gvc_level_bar_get_rms_adjustment (GvcLevelBar *bar); - -void gvc_level_bar_set_scale (GvcLevelBar *bar, - GvcLevelScale scale); - -G_END_DECLS - -#endif /* __GVC_LEVEL_BAR_H */ diff --git a/mate-volume-control/src/gvc-mixer-dialog.c b/mate-volume-control/src/gvc-mixer-dialog.c deleted file mode 100644 index b6525fb..0000000 --- a/mate-volume-control/src/gvc-mixer-dialog.c +++ /dev/null @@ -1,2606 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 William Jon McCann - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "gvc-channel-bar.h" -#include "gvc-balance-bar.h" -#include "gvc-combo-box.h" -#include "gvc-mixer-dialog.h" -#include "gvc-sound-theme-chooser.h" -#include "gvc-level-bar.h" -#include "gvc-speaker-test.h" -#include "gvc-utils.h" - -#define GVC_MIXER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogPrivate)) - -struct _GvcMixerDialogPrivate -{ - MateMixerContext *context; - GHashTable *bars; - GtkWidget *notebook; - GtkWidget *output_bar; - GtkWidget *input_bar; - GtkWidget *input_level_bar; - GtkWidget *effects_bar; - GtkWidget *output_stream_box; - GtkWidget *sound_effects_box; - GtkWidget *hw_box; - GtkWidget *hw_treeview; - GtkWidget *hw_settings_box; - GtkWidget *hw_profile_combo; - GtkWidget *input_box; - GtkWidget *output_box; - GtkWidget *applications_box; - GtkWidget *applications_window; - GtkWidget *no_apps_label; - GtkWidget *output_treeview; - GtkWidget *output_settings_frame; - GtkWidget *output_settings_box; - GtkWidget *output_balance_bar; - GtkWidget *output_fade_bar; - GtkWidget *output_lfe_bar; - GtkWidget *output_port_combo; - GtkWidget *input_treeview; - GtkWidget *input_port_combo; - GtkWidget *input_settings_box; - GtkWidget *sound_theme_chooser; - GtkSizeGroup *size_group; - gdouble last_input_peak; - guint num_apps; -}; - -enum { - ICON_COLUMN, - NAME_COLUMN, - LABEL_COLUMN, - ACTIVE_COLUMN, - SPEAKERS_COLUMN, - NUM_COLUMNS -}; - -enum { - HW_ICON_COLUMN, - HW_NAME_COLUMN, - HW_LABEL_COLUMN, - HW_STATUS_COLUMN, - HW_PROFILE_COLUMN, - HW_NUM_COLUMNS -}; - -enum { - PAGE_EFFECTS, - PAGE_HARDWARE, - PAGE_INPUT, - PAGE_OUTPUT, - PAGE_APPLICATIONS -}; - -enum { - PROP_0, - PROP_CONTEXT -}; - -static const guint tab_accel_keys[] = { - GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5 -}; - -static void gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass); -static void gvc_mixer_dialog_init (GvcMixerDialog *dialog); -static void gvc_mixer_dialog_finalize (GObject *object); - -static void add_stream (GvcMixerDialog *dialog, - MateMixerStream *stream); -static void add_application_control (GvcMixerDialog *dialog, - MateMixerStreamControl *control); -static void add_effects_control (GvcMixerDialog *dialog, - MateMixerStreamControl *control); - -static void remove_stream (GvcMixerDialog *dialog, - const gchar *name); -static void remove_application_control (GvcMixerDialog *dialog, - const gchar *name); - -static void bar_set_stream (GvcMixerDialog *dialog, - GtkWidget *bar, - MateMixerStream *stream); -static void bar_set_stream_control (GvcMixerDialog *dialog, - GtkWidget *bar, - MateMixerStreamControl *control); - -G_DEFINE_TYPE (GvcMixerDialog, gvc_mixer_dialog, GTK_TYPE_DIALOG) - -static MateMixerSwitch * -find_stream_port_switch (MateMixerStream *stream) -{ - const GList *switches; - - switches = mate_mixer_stream_list_switches (stream); - while (switches != NULL) { - MateMixerSwitch *swtch = MATE_MIXER_SWITCH (switches->data); - MateMixerSwitchFlags flags; - - flags = mate_mixer_switch_get_flags (swtch); - if ((flags & MATE_MIXER_SWITCH_TOGGLE) == 0 && - mate_mixer_switch_get_role (swtch) == MATE_MIXER_SWITCH_ROLE_PORT) - return swtch; - - switches = switches->next; - } - return NULL; -} - -static MateMixerSwitch * -find_device_profile_switch (MateMixerDevice *device) -{ - const GList *switches; - - switches = mate_mixer_device_list_switches (device); - while (switches != NULL) { - MateMixerSwitch *swtch = MATE_MIXER_SWITCH (switches->data); - - if (mate_mixer_switch_get_role (swtch) == MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE) - return swtch; - - switches = switches->next; - } - return NULL; -} - -static MateMixerStream * -find_device_test_stream (GvcMixerDialog *dialog, MateMixerDevice *device) -{ - const GList *streams; - - streams = mate_mixer_device_list_streams (device); - while (streams != NULL) { - MateMixerStream *stream; - MateMixerDirection direction; - - stream = MATE_MIXER_STREAM (streams->data); - direction = mate_mixer_stream_get_direction (stream); - - if (direction == MATE_MIXER_DIRECTION_OUTPUT) { - MateMixerStreamControl *control; - - control = mate_mixer_stream_get_default_control (stream); - if (mate_mixer_stream_control_get_num_channels (control) > 0) - return stream; - } - streams = streams->next; - } - return FALSE; -} - -static gboolean -find_tree_item_by_name (GtkTreeModel *model, - const gchar *name, - guint column, - GtkTreeIter *iter) -{ - gboolean found = FALSE; - - if (!gtk_tree_model_get_iter_first (model, iter)) - return FALSE; - - do { - gchar *n; - gtk_tree_model_get (model, iter, column, &n, -1); - - if (!g_strcmp0 (name, n)) - found = TRUE; - - g_free (n); - } while (!found && gtk_tree_model_iter_next (model, iter)); - - return found; -} - -static void -update_default_tree_item (GvcMixerDialog *dialog, - GtkTreeModel *model, - MateMixerStream *stream) -{ - GtkTreeIter iter; - const gchar *name = NULL; - - if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) - return; - - /* The supplied stream is the default, or the selected item. Traverse - * the item list and mark each item as being selected or not. Also do not - * presume some known stream is selected and allow NULL here. */ - if (stream != NULL) - name = mate_mixer_stream_get_name (stream); - - do { - gchar *n; - gtk_tree_model_get (model, &iter, - NAME_COLUMN, &n, - -1); - gtk_list_store_set (GTK_LIST_STORE (model), - &iter, - ACTIVE_COLUMN, !g_strcmp0 (name, n), - -1); - g_free (n); - } while (gtk_tree_model_iter_next (model, &iter)); -} - -static void -on_combobox_option_changed (GvcComboBox *combo, - const gchar *name, - GvcMixerDialog *dialog) -{ - MateMixerSwitch *swtch; - MateMixerSwitchOption *option; - const GList *options; - - swtch = g_object_get_data (G_OBJECT (combo), "switch"); - if (G_UNLIKELY (swtch == NULL)) { - g_warn_if_reached (); - return; - } - - options = mate_mixer_switch_list_options (swtch); - while (options != NULL) { - option = MATE_MIXER_SWITCH_OPTION (options->data); - - if (g_strcmp0 (mate_mixer_switch_option_get_name (option), name) == 0) - break; - - option = NULL; - options = options->next; - } - - if (G_UNLIKELY (option == NULL)) { - g_warn_if_reached (); - return; - } - - mate_mixer_switch_set_active_option (swtch, option); -} - -static GtkWidget * -create_port_combo_box (GvcMixerDialog *dialog, - MateMixerSwitch *swtch, - const gchar *name, - const GList *items, - const gchar *active) -{ - GtkWidget *combobox; - - combobox = gvc_combo_box_new (name); - - gvc_combo_box_set_options (GVC_COMBO_BOX (combobox), items); - gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), active); - - gvc_combo_box_set_size_group (GVC_COMBO_BOX (combobox), - dialog->priv->size_group, - FALSE); - - g_object_set_data_full (G_OBJECT (combobox), - "switch", - g_object_ref (swtch), - g_object_unref); - - g_signal_connect (G_OBJECT (combobox), - "changed", - G_CALLBACK (on_combobox_option_changed), - dialog); - return combobox; -} - -static void -update_output_settings (GvcMixerDialog *dialog) -{ - MateMixerStream *stream; - MateMixerStreamControl *control; - MateMixerStreamControlFlags flags; - MateMixerSwitch *port_switch; - gboolean has_settings = FALSE; - - g_debug ("Updating output settings"); - - if (dialog->priv->output_balance_bar != NULL) { - gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), - dialog->priv->output_balance_bar); - - dialog->priv->output_balance_bar = NULL; - } - if (dialog->priv->output_fade_bar != NULL) { - gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), - dialog->priv->output_fade_bar); - - dialog->priv->output_fade_bar = NULL; - } - if (dialog->priv->output_lfe_bar != NULL) { - gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), - dialog->priv->output_lfe_bar); - - dialog->priv->output_lfe_bar = NULL; - } - if (dialog->priv->output_port_combo != NULL) { - gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), - dialog->priv->output_port_combo); - - dialog->priv->output_port_combo = NULL; - } - - /* Get the control currently associated with the output slider */ - control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->output_bar)); - if (control == NULL) { - g_debug ("There is no control for the default output stream"); - gtk_widget_hide (dialog->priv->output_settings_frame); - return; - } - flags = mate_mixer_stream_control_get_flags (control); - - /* Enable balance bar if it is available */ - if (flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE) { - dialog->priv->output_balance_bar = - gvc_balance_bar_new (control, BALANCE_TYPE_RL); - - gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_balance_bar), - dialog->priv->size_group, - TRUE); - - gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), - dialog->priv->output_balance_bar, - FALSE, FALSE, 6); - - gtk_widget_show (dialog->priv->output_balance_bar); - has_settings = TRUE; - } - - /* Enable fade bar if it is available */ - if (flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE) { - dialog->priv->output_fade_bar = - gvc_balance_bar_new (control, BALANCE_TYPE_FR); - - gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_fade_bar), - dialog->priv->size_group, - TRUE); - - gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), - dialog->priv->output_fade_bar, - FALSE, FALSE, 6); - - gtk_widget_show (dialog->priv->output_fade_bar); - has_settings = TRUE; - } - - /* Enable subwoofer volume bar if subwoofer is available */ - if (mate_mixer_stream_control_has_channel_position (control, MATE_MIXER_CHANNEL_LFE)) { - dialog->priv->output_lfe_bar = - gvc_balance_bar_new (control, BALANCE_TYPE_LFE); - - gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_lfe_bar), - dialog->priv->size_group, - TRUE); - - gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), - dialog->priv->output_lfe_bar, - FALSE, FALSE, 6); - - gtk_widget_show (dialog->priv->output_lfe_bar); - has_settings = TRUE; - } - - /* Get owning stream of the control */ - stream = mate_mixer_stream_control_get_stream (control); - if (G_UNLIKELY (stream == NULL)) - return; - - /* Enable the port selector if the stream has one */ - port_switch = find_stream_port_switch (stream); - if (port_switch != NULL) { - const GList *options; - const gchar *active_name = NULL; - MateMixerSwitchOption *active; - - options = mate_mixer_switch_list_options (port_switch); - active = mate_mixer_switch_get_active_option (port_switch); - if (active != NULL) - active_name = mate_mixer_switch_option_get_name (active); - - dialog->priv->output_port_combo = - create_port_combo_box (dialog, - port_switch, - _("Co_nnector:"), - options, - active_name); - - gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), - dialog->priv->output_port_combo, - TRUE, FALSE, 6); - - gtk_widget_show (dialog->priv->output_port_combo); - has_settings = TRUE; - } - - if (has_settings == TRUE) - gtk_widget_show (dialog->priv->output_settings_frame); - else - gtk_widget_hide (dialog->priv->output_settings_frame); -} - -static void -set_output_stream (GvcMixerDialog *dialog, MateMixerStream *stream) -{ - GtkTreeModel *model; - MateMixerSwitch *swtch; - MateMixerStreamControl *control; - - control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->output_bar)); - if (control != NULL) { - /* Disconnect port switch of the previous stream */ - if (dialog->priv->output_port_combo != NULL) { - swtch = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo), - "switch"); - if (swtch != NULL) - g_signal_handlers_disconnect_by_data (G_OBJECT (swtch), - dialog); - } - } - - bar_set_stream (dialog, dialog->priv->output_bar, stream); - - if (stream != NULL) { - const GList *controls; - - controls = mate_mixer_context_list_stored_controls (dialog->priv->context); - - /* Move all stored controls to the newly selected default stream */ - while (controls != NULL) { - MateMixerStream *parent; - MateMixerStreamControl *control; - - control = MATE_MIXER_STREAM_CONTROL (controls->data); - parent = mate_mixer_stream_control_get_stream (control); - - /* Prefer streamless controls to stay the way they are, forcing them to - * a particular owning stream would be wrong for eg. event controls */ - if (parent != NULL && parent != stream) { - MateMixerDirection direction = - mate_mixer_stream_get_direction (parent); - - if (direction == MATE_MIXER_DIRECTION_OUTPUT) - mate_mixer_stream_control_set_stream (control, stream); - } - controls = controls->next; - } - } - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - update_default_tree_item (dialog, model, stream); - - update_output_settings (dialog); -} - -static void -on_context_default_output_stream_notify (MateMixerContext *context, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - MateMixerStream *stream; - - stream = mate_mixer_context_get_default_output_stream (context); - - set_output_stream (dialog, stream); -} - -#define DECAY_STEP .15 - -static void -on_stream_control_monitor_value (MateMixerStream *stream, - gdouble value, - GvcMixerDialog *dialog) -{ - GtkAdjustment *adj; - - if (dialog->priv->last_input_peak >= DECAY_STEP) { - if (value < dialog->priv->last_input_peak - DECAY_STEP) { - value = dialog->priv->last_input_peak - DECAY_STEP; - } - } - - dialog->priv->last_input_peak = value; - - adj = gvc_level_bar_get_peak_adjustment (GVC_LEVEL_BAR (dialog->priv->input_level_bar)); - if (value >= 0) - gtk_adjustment_set_value (adj, value); - else - gtk_adjustment_set_value (adj, 0.0); -} - -static void -update_input_settings (GvcMixerDialog *dialog) -{ - MateMixerStream *stream; - MateMixerStreamControl *control; - MateMixerStreamControlFlags flags; - MateMixerSwitch *port_switch; - - g_debug ("Updating input settings"); - - if (dialog->priv->input_port_combo != NULL) { - gtk_container_remove (GTK_CONTAINER (dialog->priv->input_settings_box), - dialog->priv->input_port_combo); - - dialog->priv->input_port_combo = NULL; - } - - /* Get the control currently associated with the input slider */ - control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); - if (control == NULL) - return; - - flags = mate_mixer_stream_control_get_flags (control); - - /* Enable level bar only if supported by the control */ - if (flags & MATE_MIXER_STREAM_CONTROL_HAS_MONITOR) - g_signal_connect (G_OBJECT (control), - "monitor-value", - G_CALLBACK (on_stream_control_monitor_value), - dialog); - - /* Get owning stream of the control */ - stream = mate_mixer_stream_control_get_stream (control); - if (G_UNLIKELY (stream == NULL)) - return; - - /* Enable the port selector if the stream has one */ - port_switch = find_stream_port_switch (stream); - if (port_switch != NULL) { - const GList *options; - const gchar *active_name = NULL; - MateMixerSwitchOption *active; - - options = mate_mixer_switch_list_options (port_switch); - active = mate_mixer_switch_get_active_option (port_switch); - if (active != NULL) - active_name = mate_mixer_switch_option_get_name (active); - - dialog->priv->input_port_combo = - create_port_combo_box (dialog, - port_switch, - _("Co_nnector:"), - options, - active_name); - - gtk_box_pack_start (GTK_BOX (dialog->priv->input_settings_box), - dialog->priv->input_port_combo, - TRUE, TRUE, 0); - - gtk_widget_show (dialog->priv->input_port_combo); - } -} - -static void -on_stream_control_mute_notify (MateMixerStreamControl *control, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - /* Stop monitoring the input stream when it gets muted */ - if (mate_mixer_stream_control_get_mute (control) == TRUE) - mate_mixer_stream_control_set_monitor_enabled (control, FALSE); - else - mate_mixer_stream_control_set_monitor_enabled (control, TRUE); -} - -static void -set_input_stream (GvcMixerDialog *dialog, MateMixerStream *stream) -{ - GtkTreeModel *model; - MateMixerSwitch *swtch; - MateMixerStreamControl *control; - - control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); - if (control != NULL) { - /* Disconnect port switch of the previous stream */ - if (dialog->priv->input_port_combo != NULL) { - swtch = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo), - "switch"); - if (swtch != NULL) - g_signal_handlers_disconnect_by_data (G_OBJECT (swtch), - dialog); - } - - /* Disable monitoring of the previous control */ - g_signal_handlers_disconnect_by_func (G_OBJECT (control), - G_CALLBACK (on_stream_control_monitor_value), - dialog); - - mate_mixer_stream_control_set_monitor_enabled (control, FALSE); - } - - bar_set_stream (dialog, dialog->priv->input_bar, stream); - - if (stream != NULL) { - const GList *controls; - guint page = gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog->priv->notebook)); - - controls = mate_mixer_context_list_stored_controls (dialog->priv->context); - - /* Move all stored controls to the newly selected default stream */ - while (controls != NULL) { - MateMixerStream *parent; - - control = MATE_MIXER_STREAM_CONTROL (controls->data); - parent = mate_mixer_stream_control_get_stream (control); - - /* Prefer streamless controls to stay the way they are, forcing them to - * a particular owning stream would be wrong for eg. event controls */ - if (parent != NULL && parent != stream) { - MateMixerDirection direction = - mate_mixer_stream_get_direction (parent); - - if (direction == MATE_MIXER_DIRECTION_INPUT) - mate_mixer_stream_control_set_stream (control, stream); - } - controls = controls->next; - } - - if (page == PAGE_INPUT) { - MateMixerStreamControl *control = - gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); - - if (G_LIKELY (control != NULL)) - mate_mixer_stream_control_set_monitor_enabled (control, TRUE); - } - - /* Enable/disable the peak level monitor according to mute state */ - g_signal_connect (G_OBJECT (stream), - "notify::mute", - G_CALLBACK (on_stream_control_mute_notify), - dialog); - } - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - update_default_tree_item (dialog, model, stream); - - update_input_settings (dialog); -} - -static void -on_context_default_input_stream_notify (MateMixerContext *context, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - MateMixerStream *stream; - - g_debug ("Default input stream has changed"); - - stream = mate_mixer_context_get_default_input_stream (context); - - set_input_stream (dialog, stream); -} - -static GvcComboBox * -find_combo_box_by_switch (GvcMixerDialog *dialog, MateMixerSwitch *swtch) -{ - MateMixerSwitch *combo_switch; - - if (dialog->priv->output_port_combo != NULL) { - combo_switch = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo), - "switch"); - if (combo_switch == swtch) - return GVC_COMBO_BOX (dialog->priv->output_port_combo); - } - - if (dialog->priv->input_port_combo != NULL) { - combo_switch = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo), - "switch"); - if (combo_switch == swtch) - return GVC_COMBO_BOX (dialog->priv->input_port_combo); - } - return NULL; -} - -static void -on_switch_option_notify (MateMixerSwitch *swtch, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - GvcComboBox *combobox; - MateMixerSwitchOption *port; - - combobox = find_combo_box_by_switch (dialog, swtch); - if (G_UNLIKELY (combobox == NULL)) { - g_warn_if_reached (); - return; - } - - g_signal_handlers_block_by_func (G_OBJECT (combobox), - on_combobox_option_changed, - dialog); - - port = mate_mixer_switch_get_active_option (swtch); - if (G_LIKELY (port != NULL)) - gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), - mate_mixer_switch_option_get_name (port)); - - g_signal_handlers_unblock_by_func (G_OBJECT (combobox), - on_combobox_option_changed, - dialog); -} - -static GtkWidget * -create_bar (GvcMixerDialog *dialog, gboolean use_size_group, gboolean symmetric) -{ - GtkWidget *bar; - - bar = gvc_channel_bar_new (NULL); - - if (use_size_group == TRUE) - gvc_channel_bar_set_size_group (GVC_CHANNEL_BAR (bar), - dialog->priv->size_group, - symmetric); - - g_object_set (G_OBJECT (bar), - "orientation", GTK_ORIENTATION_HORIZONTAL, - "show-mute", TRUE, - "show-icons", TRUE, - "show-marks", TRUE, - "extended", TRUE, NULL); - return bar; -} - -static void -bar_set_stream (GvcMixerDialog *dialog, - GtkWidget *bar, - MateMixerStream *stream) -{ - MateMixerStreamControl *control = NULL; - - if (stream != NULL) { - MateMixerSwitch *port_switch; - - control = mate_mixer_stream_get_default_control (stream); - - port_switch = find_stream_port_switch (stream); - if (port_switch != NULL) - g_signal_connect (G_OBJECT (port_switch), - "notify::active-option", - G_CALLBACK (on_switch_option_notify), - dialog); - } - - bar_set_stream_control (dialog, bar, control); -} - -static void -bar_set_stream_control (GvcMixerDialog *dialog, - GtkWidget *bar, - MateMixerStreamControl *control) -{ - MateMixerStreamControl *previous; - - previous = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (bar)); - if (previous != NULL) { - const gchar *name = mate_mixer_stream_control_get_name (previous); - - g_debug ("Disconnecting old stream control %s", name); - - g_signal_handlers_disconnect_by_data (G_OBJECT (previous), dialog); - - /* This may not do anything because we no longer have the information - * about the owning stream, in case it was an input stream, make - * sure to disconnected from the peak level monitor */ - mate_mixer_stream_control_set_monitor_enabled (previous, FALSE); - - g_hash_table_remove (dialog->priv->bars, name); - } - - gvc_channel_bar_set_control (GVC_CHANNEL_BAR (bar), control); - - if (control != NULL) { - const gchar *name = mate_mixer_stream_control_get_name (control); - - g_hash_table_insert (dialog->priv->bars, - (gpointer) name, - bar); - - gtk_widget_set_sensitive (GTK_WIDGET (bar), TRUE); - } else - gtk_widget_set_sensitive (GTK_WIDGET (bar), TRUE); -} - -static void -add_application_control (GvcMixerDialog *dialog, MateMixerStreamControl *control) -{ - MateMixerStream *stream; - MateMixerStreamControlMediaRole media_role; - MateMixerAppInfo *info; - MateMixerDirection direction = MATE_MIXER_DIRECTION_UNKNOWN; - GtkWidget *bar; - const gchar *app_id; - const gchar *app_name; - const gchar *app_icon; - - media_role = mate_mixer_stream_control_get_media_role (control); - - /* Add stream to the applications page, but make sure the stream qualifies - * for the inclusion */ - info = mate_mixer_stream_control_get_app_info (control); - if (info == NULL) - return; - - /* Skip streams with roles we don't care about */ - if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT || - media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST || - media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT || - media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER) - return; - - app_id = mate_mixer_app_info_get_id (info); - - /* These applications may have associated streams because they do peak - * level monitoring, skip these too */ - if (!g_strcmp0 (app_id, "org.mate.VolumeControl") || - !g_strcmp0 (app_id, "org.gnome.VolumeControl") || - !g_strcmp0 (app_id, "org.PulseAudio.pavucontrol")) - return; - - app_name = mate_mixer_app_info_get_name (info); - if (app_name == NULL) - app_name = mate_mixer_stream_control_get_label (control); - if (app_name == NULL) - app_name = mate_mixer_stream_control_get_name (control); - if (G_UNLIKELY (app_name == NULL)) - return; - - bar = create_bar (dialog, FALSE, FALSE); - - g_object_set (G_OBJECT (bar), - "show-marks", FALSE, - "extended", FALSE, - NULL); - - /* By default channel bars use speaker icons, use microphone icons - * instead for recording applications */ - stream = mate_mixer_stream_control_get_stream (control); - if (stream != NULL) - direction = mate_mixer_stream_get_direction (stream); - - if (direction == MATE_MIXER_DIRECTION_INPUT) - g_object_set (G_OBJECT (bar), - "low-icon-name", "audio-input-microphone-low", - "high-icon-name", "audio-input-microphone-high", - NULL); - - app_icon = mate_mixer_app_info_get_icon (info); - if (app_icon == NULL) { - if (direction == MATE_MIXER_DIRECTION_INPUT) - app_icon = "audio-input-microphone"; - else - app_icon = "applications-multimedia"; - } - - gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), app_name); - gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar), app_icon); - - gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), - bar, - FALSE, FALSE, 12); - - bar_set_stream_control (dialog, bar, control); - dialog->priv->num_apps++; - - gtk_widget_hide (dialog->priv->no_apps_label); - gtk_widget_show (bar); -} - -static void -add_effects_control (GvcMixerDialog *dialog, MateMixerStreamControl *control) -{ - MateMixerStreamControl *previous; - const gchar *name; - - /* We use a stored event control for the effects volume slider, - * because regular streams are only created when an event sound - * is played and then immediately destroyed. - * The stored event control should exist all the time. */ - previous = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->effects_bar)); - if (previous != NULL) { - name = mate_mixer_stream_control_get_name (previous); - - g_signal_handlers_disconnect_by_data (G_OBJECT (previous), dialog); - g_hash_table_remove (dialog->priv->bars, name); - } - - gvc_channel_bar_set_control (GVC_CHANNEL_BAR (dialog->priv->effects_bar), control); - - if (control != NULL) { - name = mate_mixer_stream_control_get_name (control); - g_hash_table_insert (dialog->priv->bars, - (gpointer) name, - dialog->priv->effects_bar); - - gtk_widget_set_sensitive (GTK_WIDGET (dialog->priv->effects_bar), TRUE); - } else - gtk_widget_set_sensitive (GTK_WIDGET (dialog->priv->effects_bar), FALSE); -} - -static void -on_stream_control_added (MateMixerStream *stream, - const gchar *name, - GvcMixerDialog *dialog) -{ - MateMixerStreamControl *control; - MateMixerStreamControlRole role; - - control = mate_mixer_stream_get_control (stream, name); - if G_UNLIKELY (control == NULL) - return; - - role = mate_mixer_stream_control_get_role (control); - - if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) - add_application_control (dialog, control); -} - -static void -on_stream_control_removed (MateMixerStream *stream, - const gchar *name, - GvcMixerDialog *dialog) -{ - MateMixerStreamControl *control; - - control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); - if (control != NULL) { - const gchar *input_name = mate_mixer_stream_control_get_name (control); - - if (strcmp (name, input_name) == 0) { - // XXX probably can't even happen, but handle it somehow - return; - } - } - - control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->output_bar)); - if (control != NULL) { - const gchar *input_name = mate_mixer_stream_control_get_name (control); - - if (strcmp (name, input_name) == 0) { - // XXX probably can't even happen, but handle it somehow - return; - } - } - - /* No way to be sure that it is an application control, but we don't have - * any other than application bars that could match the name */ - remove_application_control (dialog, name); -} - -static void -add_stream (GvcMixerDialog *dialog, MateMixerStream *stream) -{ - GtkTreeModel *model = NULL; - GtkTreeIter iter; - const gchar *speakers = NULL; - const GList *controls; - gboolean is_default = FALSE; - MateMixerDirection direction; - - direction = mate_mixer_stream_get_direction (stream); - - if (direction == MATE_MIXER_DIRECTION_INPUT) { - MateMixerStream *input; - - input = mate_mixer_context_get_default_input_stream (dialog->priv->context); - if (stream == input) { - bar_set_stream (dialog, dialog->priv->input_bar, stream); - - update_input_settings (dialog); - is_default = TRUE; - } - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - } - else if (direction == MATE_MIXER_DIRECTION_OUTPUT) { - MateMixerStream *output; - MateMixerStreamControl *control; - - output = mate_mixer_context_get_default_output_stream (dialog->priv->context); - if (stream == output) { - bar_set_stream (dialog, dialog->priv->output_bar, stream); - - update_output_settings (dialog); - is_default = TRUE; - } - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - - control = mate_mixer_stream_get_default_control (stream); - if (G_LIKELY (control != NULL)) - speakers = gvc_channel_map_to_pretty_string (control); - } - - controls = mate_mixer_stream_list_controls (stream); - while (controls != NULL) { - MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (controls->data); - MateMixerStreamControlRole role; - - role = mate_mixer_stream_control_get_role (control); - - if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) - add_application_control (dialog, control); - - controls = controls->next; - } - - if (model != NULL) { - const gchar *name; - const gchar *label; - - name = mate_mixer_stream_get_name (stream); - label = mate_mixer_stream_get_label (stream); - - gtk_list_store_append (GTK_LIST_STORE (model), &iter); - gtk_list_store_set (GTK_LIST_STORE (model), - &iter, - NAME_COLUMN, name, - LABEL_COLUMN, label, - ACTIVE_COLUMN, is_default, - SPEAKERS_COLUMN, speakers, - -1); - } - - // XXX find a way to disconnect when removed - g_signal_connect (G_OBJECT (stream), - "control-added", - G_CALLBACK (on_stream_control_added), - dialog); - g_signal_connect (G_OBJECT (stream), - "control-removed", - G_CALLBACK (on_stream_control_removed), - dialog); -} - -static void -update_device_test_visibility (GvcMixerDialog *dialog) -{ - MateMixerDevice *device; - MateMixerStream *stream; - - device = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), "device"); - if (G_UNLIKELY (device == NULL)) { - return; - } - - stream = find_device_test_stream (dialog, device); - - g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), - "show-button", (stream != NULL), - NULL); -} - -static void -on_context_stream_added (MateMixerContext *context, - const gchar *name, - GvcMixerDialog *dialog) -{ - MateMixerStream *stream; - MateMixerDirection direction; - GtkWidget *bar; - - stream = mate_mixer_context_get_stream (context, name); - if (G_UNLIKELY (stream == NULL)) - return; - - direction = mate_mixer_stream_get_direction (stream); - - /* If the newly added stream belongs to the currently selected device and - * the test button is hidden, this stream may be the one to allow the - * sound test and therefore we may need to enable the button */ - if (dialog->priv->hw_profile_combo != NULL && direction == MATE_MIXER_DIRECTION_OUTPUT) { - MateMixerDevice *device1; - MateMixerDevice *device2; - - device1 = mate_mixer_stream_get_device (stream); - device2 = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), - "device"); - - if (device1 == device2) { - gboolean show_button; - - g_object_get (G_OBJECT (dialog->priv->hw_profile_combo), - "show-button", &show_button, - NULL); - - if (show_button == FALSE) - update_device_test_visibility (dialog); - } - } - - bar = g_hash_table_lookup (dialog->priv->bars, name); - if (G_UNLIKELY (bar != NULL)) - return; - - add_stream (dialog, stream); -} - -static void -remove_stream (GvcMixerDialog *dialog, const gchar *name) -{ - GtkWidget *bar; - GtkTreeIter iter; - GtkTreeModel *model; - - bar = g_hash_table_lookup (dialog->priv->bars, name); - - if (bar != NULL) { - g_debug ("Removing stream %s from bar %s", - name, - gvc_channel_bar_get_name (GVC_CHANNEL_BAR (bar))); - - bar_set_stream (dialog, bar, NULL); - } - - /* Remove from any models */ - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - if (find_tree_item_by_name (GTK_TREE_MODEL (model), - name, - NAME_COLUMN, - &iter) == TRUE) - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - if (find_tree_item_by_name (GTK_TREE_MODEL (model), - name, - NAME_COLUMN, - &iter) == TRUE) - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); -} - -static void -remove_application_control (GvcMixerDialog *dialog, const gchar *name) -{ - GtkWidget *bar; - - bar = g_hash_table_lookup (dialog->priv->bars, name); - if (G_UNLIKELY (bar == NULL)) - return; - - g_debug ("Removing application stream %s", name); - - /* We could call bar_set_stream_control here, but that would pointlessly - * invalidate the channel bar, so just remove it ourselves */ - g_hash_table_remove (dialog->priv->bars, name); - - gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (bar)), bar); - - if (G_UNLIKELY (dialog->priv->num_apps <= 0)) { - g_warn_if_reached (); - dialog->priv->num_apps = 1; - } - - dialog->priv->num_apps--; - - if (dialog->priv->num_apps == 0) - gtk_widget_show (dialog->priv->no_apps_label); -} - -static void -on_context_stream_removed (MateMixerContext *context, - const gchar *name, - GvcMixerDialog *dialog) -{ - if (dialog->priv->hw_profile_combo != NULL) { - gboolean show_button; - - g_object_get (G_OBJECT (dialog->priv->hw_profile_combo), - "show-button", &show_button, - NULL); - - if (show_button == TRUE) - update_device_test_visibility (dialog); - } - - remove_stream (dialog, name); -} - -static void -on_context_stored_control_added (MateMixerContext *context, - const gchar *name, - GvcMixerDialog *dialog) -{ - MateMixerStreamControl *control; - MateMixerStreamControlMediaRole media_role; - - control = MATE_MIXER_STREAM_CONTROL (mate_mixer_context_get_stored_control (context, name)); - if (G_UNLIKELY (control == NULL)) - return; - - media_role = mate_mixer_stream_control_get_media_role (control); - - if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT) - add_effects_control (dialog, control); -} - -static void -on_context_stored_control_removed (MateMixerContext *context, - const gchar *name, - GvcMixerDialog *dialog) -{ - GtkWidget *bar; - - bar = g_hash_table_lookup (dialog->priv->bars, name); - - if (bar != NULL) { - /* We only use a stored control in the effects bar */ - if (G_UNLIKELY (bar != dialog->priv->effects_bar)) { - g_warn_if_reached (); - return; - } - - bar_set_stream (dialog, bar, NULL); - } -} - -static gchar * -device_status (MateMixerDevice *device) -{ - guint inputs = 0; - guint outputs = 0; - gchar *inputs_str = NULL; - gchar *outputs_str = NULL; - const GList *streams; - - /* Get number of input and output streams in the device */ - streams = mate_mixer_device_list_streams (device); - while (streams != NULL) { - MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); - MateMixerDirection direction; - - direction = mate_mixer_stream_get_direction (stream); - - if (direction == MATE_MIXER_DIRECTION_INPUT) - inputs++; - else if (direction == MATE_MIXER_DIRECTION_OUTPUT) - outputs++; - - streams = streams->next; - } - - if (inputs == 0 && outputs == 0) { - /* translators: - * The device has been disabled */ - return g_strdup (_("Disabled")); - } - - if (outputs > 0) { - /* translators: - * The number of sound outputs on a particular device */ - outputs_str = g_strdup_printf (ngettext ("%u Output", - "%u Outputs", - outputs), - outputs); - } - - if (inputs > 0) { - /* translators: - * The number of sound inputs on a particular device */ - inputs_str = g_strdup_printf (ngettext ("%u Input", - "%u Inputs", - inputs), - inputs); - } - - if (inputs_str != NULL && outputs_str != NULL) { - gchar *ret = g_strdup_printf ("%s / %s", - outputs_str, - inputs_str); - g_free (outputs_str); - g_free (inputs_str); - return ret; - } - - if (inputs_str != NULL) - return inputs_str; - - return outputs_str; -} - -static void -update_device_info (GvcMixerDialog *dialog, MateMixerDevice *device) -{ - GtkTreeModel *model = NULL; - GtkTreeIter iter; - const gchar *label; - const gchar *profile_label = NULL; - gchar *status; - MateMixerSwitch *profile_switch; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); - - if (find_tree_item_by_name (model, - mate_mixer_device_get_name (device), - HW_NAME_COLUMN, - &iter) == FALSE) - return; - - label = mate_mixer_device_get_label (device); - - profile_switch = find_device_profile_switch (device); - if (profile_switch != NULL) { - MateMixerSwitchOption *active; - - active = mate_mixer_switch_get_active_option (profile_switch); - if (G_LIKELY (active != NULL)) - profile_label = mate_mixer_switch_option_get_label (active); - } - - status = device_status (device); - - gtk_list_store_set (GTK_LIST_STORE (model), - &iter, - HW_LABEL_COLUMN, label, - HW_PROFILE_COLUMN, profile_label, - HW_STATUS_COLUMN, status, - -1); - g_free (status); -} - -static void -on_device_profile_notify (MateMixerSwitch *swtch, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - MateMixerSwitchOption *active; - - g_signal_handlers_block_by_func (G_OBJECT (dialog->priv->hw_profile_combo), - G_CALLBACK (on_combobox_option_changed), - dialog); - - active = mate_mixer_switch_get_active_option (swtch); - if (G_LIKELY (active != NULL)) { - const gchar *name = mate_mixer_switch_option_get_name (active); - - gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), - name); - } - - g_signal_handlers_unblock_by_func (G_OBJECT (dialog->priv->hw_profile_combo), - G_CALLBACK (on_combobox_option_changed), - dialog); - - // XXX find device - // update_device_info (dialog, device); -} - -static void -add_device (GvcMixerDialog *dialog, MateMixerDevice *device) -{ - GtkTreeModel *model; - GtkTreeIter iter; - GIcon *icon; - const gchar *name; - const gchar *label; - gchar *status; - const gchar *profile_label = NULL; - MateMixerSwitch *profile_switch; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); - - name = mate_mixer_device_get_name (device); - label = mate_mixer_device_get_label (device); - - if (find_tree_item_by_name (GTK_TREE_MODEL (model), - name, - HW_NAME_COLUMN, - &iter) == FALSE) - gtk_list_store_append (GTK_LIST_STORE (model), &iter); - - icon = g_themed_icon_new_with_default_fallbacks (mate_mixer_device_get_icon (device)); - - profile_switch = find_device_profile_switch (device); - if (profile_switch != NULL) { - MateMixerSwitchOption *active; - - active = mate_mixer_switch_get_active_option (profile_switch); - if (G_LIKELY (active != NULL)) - profile_label = mate_mixer_switch_option_get_label (active); - - g_signal_connect (G_OBJECT (device), - "notify::active-option", - G_CALLBACK (on_device_profile_notify), - dialog); - } - - status = device_status (device); - - gtk_list_store_set (GTK_LIST_STORE (model), - &iter, - HW_NAME_COLUMN, name, - HW_LABEL_COLUMN, label, - HW_ICON_COLUMN, icon, - HW_PROFILE_COLUMN, profile_label, - HW_STATUS_COLUMN, status, - -1); - g_free (status); - -} - -static void -on_context_device_added (MateMixerContext *context, const gchar *name, GvcMixerDialog *dialog) -{ - MateMixerDevice *device; - - device = mate_mixer_context_get_device (context, name); - if (G_UNLIKELY (device == NULL)) - return; - - add_device (dialog, device); -} - -static void -on_context_device_removed (MateMixerContext *context, - const gchar *name, - GvcMixerDialog *dialog) -{ - GtkTreeIter iter; - GtkTreeModel *model; - - /* Remove from the device model */ - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); - - if (find_tree_item_by_name (GTK_TREE_MODEL (model), - name, - HW_NAME_COLUMN, - &iter) == TRUE) - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); -} - -static void -make_label_bold (GtkLabel *label) -{ - PangoFontDescription *font_desc; - - font_desc = pango_font_description_new (); - - pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); - - /* This will only affect the weight of the font, the rest is - * from the current state of the widget, which comes from the - * theme or user prefs, since the font desc only has the - * weight flag turned on. */ -#if GTK_CHECK_VERSION (3, 0, 0) - gtk_widget_override_font (GTK_WIDGET (label), font_desc); -#else - gtk_widget_modify_font (GTK_WIDGET (label), font_desc); -#endif - pango_font_description_free (font_desc); -} - -static void -on_input_radio_toggled (GtkCellRendererToggle *renderer, - gchar *path_str, - GvcMixerDialog *dialog) -{ - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreePath *path; - gboolean toggled = FALSE; - gchar *name = NULL; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - path = gtk_tree_path_new_from_string (path_str); - - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_path_free (path); - - gtk_tree_model_get (model, &iter, - NAME_COLUMN, &name, - ACTIVE_COLUMN, &toggled, - -1); - if (toggled ^ 1) { - MateMixerStream *stream; - MateMixerBackendFlags flags; - - stream = mate_mixer_context_get_stream (dialog->priv->context, name); - if (G_UNLIKELY (stream == NULL)) { - g_warn_if_reached (); - g_free (name); - return; - } - - g_debug ("Default input stream selection changed to %s", name); - - // XXX cache this - flags = mate_mixer_context_get_backend_flags (dialog->priv->context); - - if (flags & MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM) - mate_mixer_context_set_default_input_stream (dialog->priv->context, stream); - else - set_input_stream (dialog, stream); - } - g_free (name); -} - -static void -on_output_radio_toggled (GtkCellRendererToggle *renderer, - gchar *path_str, - GvcMixerDialog *dialog) -{ - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreePath *path; - gboolean toggled = FALSE; - gchar *name = NULL; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - path = gtk_tree_path_new_from_string (path_str); - - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_path_free (path); - - gtk_tree_model_get (model, &iter, - NAME_COLUMN, &name, - ACTIVE_COLUMN, &toggled, - -1); - if (toggled ^ 1) { - MateMixerStream *stream; - MateMixerBackendFlags flags; - - stream = mate_mixer_context_get_stream (dialog->priv->context, name); - if (G_UNLIKELY (stream == NULL)) { - g_warn_if_reached (); - g_free (name); - return; - } - - g_debug ("Default output stream selection changed to %s", name); - - // XXX cache this - flags = mate_mixer_context_get_backend_flags (dialog->priv->context); - - if (flags & MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM) - mate_mixer_context_set_default_output_stream (dialog->priv->context, stream); - else - set_output_stream (dialog, stream); - } - g_free (name); -} - -static void -stream_name_to_text (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - gchar *label; - gchar *speakers; - - gtk_tree_model_get (model, iter, - LABEL_COLUMN, &label, - SPEAKERS_COLUMN, &speakers, - -1); - - if (speakers != NULL) { - gchar *str = g_strdup_printf ("%s\n%s", - label, - speakers); - - g_object_set (cell, "markup", str, NULL); - g_free (str); - g_free (speakers); - } else { - g_object_set (cell, "text", label, NULL); - } - - g_free (label); -} - -static gint -compare_stream_treeview_items (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - gchar *desc_a = NULL; - gchar *desc_b = NULL; - gint result; - - gtk_tree_model_get (model, a, - LABEL_COLUMN, &desc_a, - -1); - gtk_tree_model_get (model, b, - LABEL_COLUMN, &desc_b, - -1); - - if (desc_a == NULL) { - g_free (desc_b); - return -1; - } - if (desc_b == NULL) { - g_free (desc_a); - return 1; - } - - result = g_ascii_strcasecmp (desc_a, desc_b); - - g_free (desc_a); - g_free (desc_b); - return result; -} - -static GtkWidget * -create_stream_treeview (GvcMixerDialog *dialog, GCallback on_toggled) -{ - GtkWidget *treeview; - GtkListStore *store; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - treeview = gtk_tree_view_new (); - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); - - store = gtk_list_store_new (NUM_COLUMNS, - G_TYPE_ICON, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_BOOLEAN, - G_TYPE_STRING); - - gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), - GTK_TREE_MODEL (store)); - - renderer = gtk_cell_renderer_toggle_new (); - gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE); - - column = gtk_tree_view_column_new_with_attributes (NULL, - renderer, - "active", ACTIVE_COLUMN, - NULL); - - gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); - - g_signal_connect (G_OBJECT (renderer), - "toggled", - G_CALLBACK (on_toggled), - dialog); - - gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1, - _("Name"), - gtk_cell_renderer_text_new (), - stream_name_to_text, - NULL, NULL); - - /* Keep the list of streams sorted by the name */ - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), - LABEL_COLUMN, - GTK_SORT_ASCENDING); - - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), - LABEL_COLUMN, - compare_stream_treeview_items, - NULL, NULL); - return treeview; -} - -static void -on_test_speakers_clicked (GvcComboBox *widget, GvcMixerDialog *dialog) -{ - GtkWidget *d, - *test, - *container; - gchar *title; - MateMixerDevice *device; - MateMixerStream *stream; - - device = g_object_get_data (G_OBJECT (widget), "device"); - if (G_UNLIKELY (device == NULL)) { - g_warn_if_reached (); - return; - } - - stream = find_device_test_stream (dialog, device); - if (G_UNLIKELY (stream == NULL)) { - g_warn_if_reached (); - return; - } - - title = g_strdup_printf (_("Speaker Testing for %s"), - mate_mixer_device_get_label (device)); - - d = gtk_dialog_new_with_buttons (title, - GTK_WINDOW (dialog), - GTK_DIALOG_MODAL | - GTK_DIALOG_DESTROY_WITH_PARENT, - "gtk-close", - GTK_RESPONSE_CLOSE, - NULL); - g_free (title); - - gtk_window_set_resizable (GTK_WINDOW (d), FALSE); - - test = gvc_speaker_test_new (stream); - gtk_widget_show (test); - - container = gtk_dialog_get_content_area (GTK_DIALOG (d)); - gtk_container_add (GTK_CONTAINER (container), test); - - gtk_dialog_run (GTK_DIALOG (d)); - gtk_widget_destroy (d); -} - -static GtkWidget * -create_profile_combo_box (GvcMixerDialog *dialog, - MateMixerSwitch *swtch, - const gchar *name, - const GList *items, - const gchar *active) -{ - GtkWidget *combobox; - - combobox = gvc_combo_box_new (name); - - gvc_combo_box_set_options (GVC_COMBO_BOX (combobox), items); - gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), active); - - gvc_combo_box_set_size_group (GVC_COMBO_BOX (combobox), - dialog->priv->size_group, - FALSE); - - g_object_set (G_OBJECT (combobox), - "button-label", _("Test Speakers"), - NULL); - - g_object_set_data_full (G_OBJECT (combobox), - "switch", - g_object_ref (swtch), - g_object_unref); - - g_signal_connect (G_OBJECT (combobox), - "changed", - G_CALLBACK (on_combobox_option_changed), - dialog); - - g_signal_connect (G_OBJECT (combobox), - "button-clicked", - G_CALLBACK (on_test_speakers_clicked), - dialog); - - return combobox; -} - -static void -on_device_selection_changed (GtkTreeSelection *selection, GvcMixerDialog *dialog) -{ - GtkTreeIter iter; - gchar *name; - MateMixerDevice *device; - MateMixerSwitch *profile_switch; - - g_debug ("Device selection changed"); - - if (dialog->priv->hw_profile_combo != NULL) { - gtk_container_remove (GTK_CONTAINER (dialog->priv->hw_settings_box), - dialog->priv->hw_profile_combo); - - dialog->priv->hw_profile_combo = NULL; - } - - if (gtk_tree_selection_get_selected (selection, NULL, &iter) == FALSE) - return; - - gtk_tree_model_get (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)), - &iter, - HW_NAME_COLUMN, &name, - -1); - - device = mate_mixer_context_get_device (dialog->priv->context, name); - if (G_UNLIKELY (device == NULL)) { - g_warn_if_reached (); - g_free (name); - return; - } - g_free (name); - - /* Profile/speaker test combo */ - profile_switch = find_device_profile_switch (device); - if (profile_switch != NULL) { - const GList *options; - const gchar *active_name = NULL; - MateMixerSwitchOption *active; - - options = mate_mixer_switch_list_options (profile_switch); - active = mate_mixer_switch_get_active_option (profile_switch); - if (active != NULL) - active_name = mate_mixer_switch_option_get_name (active); - - dialog->priv->hw_profile_combo = - create_profile_combo_box (dialog, - profile_switch, - _("_Profile:"), - options, - active_name); - - g_object_set_data_full (G_OBJECT (dialog->priv->hw_profile_combo), - "device", - g_object_ref (device), - g_object_unref); - - gtk_box_pack_start (GTK_BOX (dialog->priv->hw_settings_box), - dialog->priv->hw_profile_combo, - TRUE, TRUE, 6); - - /* Enable the speaker test button if the selected device - * is capable of sound output */ - update_device_test_visibility (dialog); - - gtk_widget_show (dialog->priv->hw_profile_combo); - } -} - -static void -on_notebook_switch_page (GtkNotebook *notebook, - GtkWidget *page, - guint page_num, - GvcMixerDialog *dialog) -{ - MateMixerStreamControl *control; - - // XXX because this is called too early in constructor - if (G_UNLIKELY (dialog->priv->input_bar == NULL)) - return; - - control = gvc_channel_bar_get_control (GVC_CHANNEL_BAR (dialog->priv->input_bar)); - if (control == NULL) - return; - - if (page_num == PAGE_INPUT) - mate_mixer_stream_control_set_monitor_enabled (control, TRUE); - else - mate_mixer_stream_control_set_monitor_enabled (control, FALSE); -} - -static void -device_name_to_text (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - gchar *label = NULL; - gchar *profile = NULL; - gchar *status = NULL; - - gtk_tree_model_get (model, iter, - HW_LABEL_COLUMN, &label, - HW_PROFILE_COLUMN, &profile, - HW_STATUS_COLUMN, &status, - -1); - - if (G_LIKELY (status != NULL)) { - gchar *str; - - if (profile != NULL) - str = g_strdup_printf ("%s\n%s\n%s", - label, - status, - profile); - else - str = g_strdup_printf ("%s\n%s", - label, - status); - - g_object_set (cell, "markup", str, NULL); - g_free (str); - g_free (profile); - g_free (status); - } else - g_object_set (cell, "text", label, NULL); - - g_free (label); -} - -static gint -compare_device_treeview_items (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - gchar *desc_a = NULL; - gchar *desc_b = NULL; - gint result; - - gtk_tree_model_get (model, a, - HW_LABEL_COLUMN, &desc_a, - -1); - gtk_tree_model_get (model, b, - HW_LABEL_COLUMN, &desc_b, - -1); - - result = g_ascii_strcasecmp (desc_a, desc_b); - - g_free (desc_a); - g_free (desc_b); - return result; -} - -static GtkWidget * -create_device_treeview (GvcMixerDialog *dialog, GCallback on_changed) -{ - GtkWidget *treeview; - GtkListStore *store; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - - treeview = gtk_tree_view_new (); - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); - g_signal_connect (G_OBJECT (selection), - "changed", - on_changed, - dialog); - - store = gtk_list_store_new (HW_NUM_COLUMNS, - G_TYPE_ICON, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING); - - gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), - GTK_TREE_MODEL (store)); - - renderer = gtk_cell_renderer_pixbuf_new (); - g_object_set (G_OBJECT (renderer), - "stock-size", - GTK_ICON_SIZE_DIALOG, - NULL); - - column = gtk_tree_view_column_new_with_attributes (NULL, - renderer, - "gicon", HW_ICON_COLUMN, - NULL); - - gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); - gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1, - _("Name"), - gtk_cell_renderer_text_new (), - device_name_to_text, - NULL, NULL); - - /* Keep the list of streams sorted by the name */ - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), - HW_LABEL_COLUMN, - GTK_SORT_ASCENDING); - - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), - HW_LABEL_COLUMN, - compare_device_treeview_items, - NULL, NULL); - return treeview; -} - -static void -dialog_accel_cb (GtkAccelGroup *accelgroup, - GObject *object, - guint key, - GdkModifierType mod, - GvcMixerDialog *self) -{ - gint num = -1; - gint i; - - for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { - if (tab_accel_keys[i] == key) { - num = i; - break; - } - } - - if (num != -1) - gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num); -} - -static GObject * -gvc_mixer_dialog_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_params) -{ - GObject *object; - GvcMixerDialog *self; - GtkWidget *main_vbox; - GtkWidget *label; - GtkWidget *alignment; - GtkWidget *box; - GtkWidget *sbox; - GtkWidget *ebox; - GtkTreeSelection *selection; - GtkAccelGroup *accel_group; - GClosure *closure; - GtkTreeIter iter; - gint i; - const GList *list; - - object = G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->constructor (type, - n_construct_properties, - construct_params); - - self = GVC_MIXER_DIALOG (object); - - gtk_dialog_add_button (GTK_DIALOG (self), "gtk-close", GTK_RESPONSE_OK); - - main_vbox = gtk_dialog_get_content_area (GTK_DIALOG (self)); - gtk_box_set_spacing (GTK_BOX (main_vbox), 2); - - gtk_container_set_border_width (GTK_CONTAINER (self), 6); - -#if GTK_CHECK_VERSION (3, 0, 0) - self->priv->output_stream_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); -#else - self->priv->output_stream_box = gtk_hbox_new (FALSE, 12); -#endif - - alignment = gtk_alignment_new (0, 0, 1, 1); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0); - gtk_container_add (GTK_CONTAINER (alignment), self->priv->output_stream_box); - gtk_box_pack_start (GTK_BOX (main_vbox), - alignment, - FALSE, FALSE, 0); - - self->priv->output_bar = create_bar (self, TRUE, TRUE); - gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->output_bar), - _("_Output volume: ")); - - gtk_widget_show (self->priv->output_bar); - gtk_widget_set_sensitive (self->priv->output_bar, FALSE); - - gtk_box_pack_start (GTK_BOX (self->priv->output_stream_box), - self->priv->output_bar, TRUE, TRUE, 12); - - self->priv->notebook = gtk_notebook_new (); - gtk_box_pack_start (GTK_BOX (main_vbox), - self->priv->notebook, - TRUE, TRUE, 0); - - g_signal_connect (G_OBJECT (self->priv->notebook), - "switch-page", - G_CALLBACK (on_notebook_switch_page), - self); - - gtk_container_set_border_width (GTK_CONTAINER (self->priv->notebook), 5); - - /* Set up accels (borrowed from Empathy) */ - accel_group = gtk_accel_group_new (); - gtk_window_add_accel_group (GTK_WINDOW (self), accel_group); - - for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { - closure = g_cclosure_new (G_CALLBACK (dialog_accel_cb), - self, - NULL); - gtk_accel_group_connect (accel_group, - tab_accel_keys[i], - GDK_MOD1_MASK, - 0, - closure); - } - - g_object_unref (accel_group); - - /* Create notebook pages */ -#if GTK_CHECK_VERSION (3, 0, 0) - self->priv->sound_effects_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); -#else - self->priv->sound_effects_box = gtk_vbox_new (FALSE, 6); -#endif - gtk_container_set_border_width (GTK_CONTAINER (self->priv->sound_effects_box), 12); - - label = gtk_label_new (_("Sound Effects")); - gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), - self->priv->sound_effects_box, - label); - - self->priv->effects_bar = create_bar (self, TRUE, TRUE); - gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box), - self->priv->effects_bar, FALSE, FALSE, 0); - - gvc_channel_bar_set_show_marks (GVC_CHANNEL_BAR (self->priv->effects_bar), FALSE); - gvc_channel_bar_set_extended (GVC_CHANNEL_BAR (self->priv->effects_bar), FALSE); - - gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->effects_bar), - _("_Alert volume: ")); - - gtk_widget_show (self->priv->effects_bar); - gtk_widget_set_sensitive (self->priv->effects_bar, FALSE); - - self->priv->sound_theme_chooser = gvc_sound_theme_chooser_new (); - - gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box), - self->priv->sound_theme_chooser, - TRUE, TRUE, 6); - -#if GTK_CHECK_VERSION (3, 0, 0) - self->priv->hw_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); -#else - self->priv->hw_box = gtk_vbox_new (FALSE, 12); -#endif - gtk_container_set_border_width (GTK_CONTAINER (self->priv->hw_box), 12); - - label = gtk_label_new (_("Hardware")); - gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), - self->priv->hw_box, - label); - - box = gtk_frame_new (_("C_hoose a device to configure:")); - label = gtk_frame_get_label_widget (GTK_FRAME (box)); - make_label_bold (GTK_LABEL (label)); - gtk_label_set_use_underline (GTK_LABEL (label), TRUE); - gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, TRUE, TRUE, 0); - - alignment = gtk_alignment_new (0, 0, 1, 1); - gtk_container_add (GTK_CONTAINER (box), alignment); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); - - self->priv->hw_treeview = create_device_treeview (self, - G_CALLBACK (on_device_selection_changed)); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->hw_treeview); - - box = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box), - GTK_SHADOW_IN); - gtk_container_add (GTK_CONTAINER (box), self->priv->hw_treeview); - gtk_container_add (GTK_CONTAINER (alignment), box); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview)); - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); - - box = gtk_frame_new (_("Settings for the selected device:")); - label = gtk_frame_get_label_widget (GTK_FRAME (box)); - make_label_bold (GTK_LABEL (label)); - gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, FALSE, TRUE, 12); - -#if GTK_CHECK_VERSION (3, 0, 0) - self->priv->hw_settings_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); -#else - self->priv->hw_settings_box = gtk_vbox_new (FALSE, 12); -#endif - - gtk_container_add (GTK_CONTAINER (box), self->priv->hw_settings_box); - -#if GTK_CHECK_VERSION (3, 0, 0) - self->priv->input_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); -#else - self->priv->input_box = gtk_vbox_new (FALSE, 12); -#endif - - gtk_container_set_border_width (GTK_CONTAINER (self->priv->input_box), 12); - - label = gtk_label_new (_("Input")); - gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), - self->priv->input_box, - label); - - self->priv->input_bar = create_bar (self, TRUE, TRUE); - - gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->input_bar), - _("_Input volume: ")); - - gvc_channel_bar_set_low_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar), - "audio-input-microphone-low"); - gvc_channel_bar_set_high_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar), - "audio-input-microphone-high"); - - gtk_widget_show (self->priv->input_bar); - gtk_widget_set_sensitive (self->priv->input_bar, FALSE); - - alignment = gtk_alignment_new (0, 0, 1, 1); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); - gtk_container_add (GTK_CONTAINER (alignment), self->priv->input_bar); - gtk_box_pack_start (GTK_BOX (self->priv->input_box), - alignment, - FALSE, FALSE, 0); - -#if GTK_CHECK_VERSION (3, 0, 0) - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - sbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - ebox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); -#else - box = gtk_hbox_new (FALSE, 6); - sbox = gtk_hbox_new (FALSE, 6); - ebox = gtk_hbox_new (FALSE, 6); -#endif - - gtk_box_pack_start (GTK_BOX (self->priv->input_box), - box, - FALSE, FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), - sbox, - FALSE, FALSE, 0); - - label = gtk_label_new (_("Input level:")); - gtk_box_pack_start (GTK_BOX (sbox), - label, - FALSE, FALSE, 0); - gtk_size_group_add_widget (self->priv->size_group, sbox); - - self->priv->input_level_bar = gvc_level_bar_new (); - gvc_level_bar_set_orientation (GVC_LEVEL_BAR (self->priv->input_level_bar), - GTK_ORIENTATION_HORIZONTAL); - gvc_level_bar_set_scale (GVC_LEVEL_BAR (self->priv->input_level_bar), - GVC_LEVEL_SCALE_LINEAR); - gtk_box_pack_start (GTK_BOX (box), - self->priv->input_level_bar, - TRUE, TRUE, 6); - - gtk_box_pack_start (GTK_BOX (box), - ebox, - FALSE, FALSE, 0); - gtk_size_group_add_widget (self->priv->size_group, ebox); - -#if GTK_CHECK_VERSION (3, 0, 0) - self->priv->input_settings_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); -#else - self->priv->input_settings_box = gtk_hbox_new (FALSE, 6); -#endif - gtk_box_pack_start (GTK_BOX (self->priv->input_box), - self->priv->input_settings_box, - FALSE, FALSE, 0); - - box = gtk_frame_new (_("C_hoose a device for sound input:")); - label = gtk_frame_get_label_widget (GTK_FRAME (box)); - make_label_bold (GTK_LABEL (label)); - gtk_label_set_use_underline (GTK_LABEL (label), TRUE); - gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (self->priv->input_box), box, TRUE, TRUE, 0); - - alignment = gtk_alignment_new (0, 0, 1, 1); - gtk_container_add (GTK_CONTAINER (box), alignment); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); - - self->priv->input_treeview = - create_stream_treeview (self, G_CALLBACK (on_input_radio_toggled)); - - gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->input_treeview); - - box = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box), - GTK_SHADOW_IN); - gtk_container_add (GTK_CONTAINER (box), self->priv->input_treeview); - gtk_container_add (GTK_CONTAINER (alignment), box); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->input_treeview)); - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); - - /* Output page */ - self->priv->output_box = gtk_vbox_new (FALSE, 12); - gtk_container_set_border_width (GTK_CONTAINER (self->priv->output_box), 12); - label = gtk_label_new (_("Output")); - gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), - self->priv->output_box, - label); - - box = gtk_frame_new (_("C_hoose a device for sound output:")); - label = gtk_frame_get_label_widget (GTK_FRAME (box)); - make_label_bold (GTK_LABEL (label)); - gtk_label_set_use_underline (GTK_LABEL (label), TRUE); - gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, TRUE, TRUE, 0); - - alignment = gtk_alignment_new (0, 0, 1, 1); - gtk_container_add (GTK_CONTAINER (box), alignment); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); - - self->priv->output_treeview = create_stream_treeview (self, - G_CALLBACK (on_output_radio_toggled)); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->output_treeview); - - box = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box), - GTK_SHADOW_IN); - gtk_container_add (GTK_CONTAINER (box), self->priv->output_treeview); - gtk_container_add (GTK_CONTAINER (alignment), box); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->output_treeview)); - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); - - box = gtk_frame_new (_("Settings for the selected device:")); - label = gtk_frame_get_label_widget (GTK_FRAME (box)); - make_label_bold (GTK_LABEL (label)); - gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, FALSE, FALSE, 12); - self->priv->output_settings_box = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (box), self->priv->output_settings_box); - - self->priv->output_settings_frame = box; - - /* Applications */ - self->priv->applications_window = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->applications_window), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self->priv->applications_window), - GTK_SHADOW_IN); - -#if GTK_CHECK_VERSION (3, 0, 0) - self->priv->applications_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); -#else - self->priv->applications_box = gtk_vbox_new (FALSE, 12); -#endif - gtk_container_set_border_width (GTK_CONTAINER (self->priv->applications_box), 12); - -#if GTK_CHECK_VERSION (3, 8, 0) - gtk_container_add (GTK_CONTAINER (self->priv->applications_window), - self->priv->applications_box); -#else - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (self->priv->applications_window), - self->priv->applications_box); -#endif - - label = gtk_label_new (_("Applications")); - gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), - self->priv->applications_window, - label); - - self->priv->no_apps_label = gtk_label_new (_("No application is currently playing or recording audio.")); - gtk_box_pack_start (GTK_BOX (self->priv->applications_box), - self->priv->no_apps_label, - TRUE, TRUE, 0); - - gtk_widget_show_all (main_vbox); - - list = mate_mixer_context_list_streams (self->priv->context); - while (list != NULL) { - add_stream (self, MATE_MIXER_STREAM (list->data)); - list = list->next; - } - - list = mate_mixer_context_list_devices (self->priv->context); - while (list != NULL) { - add_device (self, MATE_MIXER_DEVICE (list->data)); - list = list->next; - } - - /* Find an event role stream */ - list = mate_mixer_context_list_stored_controls (self->priv->context); - while (list != NULL) { - MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data); - MateMixerStreamControlMediaRole media_role; - - media_role = mate_mixer_stream_control_get_media_role (control); - - if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT) { - add_effects_control (self, control); - break; - } - list = list->next; - } - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview)); - - /* Select the first device in the list */ - // XXX handle no devices - if (gtk_tree_selection_get_selected (selection, NULL, NULL) == FALSE) { - GtkTreeModel *model = - gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->hw_treeview)); - - if (gtk_tree_model_get_iter_first (model, &iter)) - gtk_tree_selection_select_iter (selection, &iter); - } - - return object; -} - -static MateMixerContext * -gvc_mixer_dialog_get_context (GvcMixerDialog *dialog) -{ - return dialog->priv->context; -} - -static void -gvc_mixer_dialog_set_context (GvcMixerDialog *dialog, MateMixerContext *context) -{ - dialog->priv->context = g_object_ref (context); - - g_signal_connect (G_OBJECT (dialog->priv->context), - "stream-added", - G_CALLBACK (on_context_stream_added), - dialog); - g_signal_connect (G_OBJECT (dialog->priv->context), - "stream-removed", - G_CALLBACK (on_context_stream_removed), - dialog); - - g_signal_connect (G_OBJECT (dialog->priv->context), - "device-added", - G_CALLBACK (on_context_device_added), - dialog); - g_signal_connect (G_OBJECT (dialog->priv->context), - "device-removed", - G_CALLBACK (on_context_device_removed), - dialog); - - g_signal_connect (G_OBJECT (dialog->priv->context), - "notify::default-input-stream", - G_CALLBACK (on_context_default_input_stream_notify), - dialog); - g_signal_connect (G_OBJECT (dialog->priv->context), - "notify::default-output-stream", - G_CALLBACK (on_context_default_output_stream_notify), - dialog); - - g_signal_connect (G_OBJECT (dialog->priv->context), - "stored-control-added", - G_CALLBACK (on_context_stored_control_added), - dialog); - g_signal_connect (G_OBJECT (dialog->priv->context), - "stored-control-removed", - G_CALLBACK (on_context_stored_control_removed), - dialog); - - g_object_notify (G_OBJECT (dialog), "context"); -} - -static void -gvc_mixer_dialog_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GvcMixerDialog *self = GVC_MIXER_DIALOG (object); - - switch (prop_id) { - case PROP_CONTEXT: - gvc_mixer_dialog_set_context (self, g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_mixer_dialog_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GvcMixerDialog *self = GVC_MIXER_DIALOG (object); - - switch (prop_id) { - case PROP_CONTEXT: - g_value_set_object (value, gvc_mixer_dialog_get_context (self)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_mixer_dialog_dispose (GObject *object) -{ - GvcMixerDialog *dialog = GVC_MIXER_DIALOG (object); - - if (dialog->priv->context != NULL) { - g_signal_handlers_disconnect_by_data (G_OBJECT (dialog->priv->context), - dialog); - - g_clear_object (&dialog->priv->context); - } - - G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->dispose (object); -} - -static void -gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructor = gvc_mixer_dialog_constructor; - object_class->dispose = gvc_mixer_dialog_dispose; - object_class->finalize = gvc_mixer_dialog_finalize; - object_class->set_property = gvc_mixer_dialog_set_property; - object_class->get_property = gvc_mixer_dialog_get_property; - - g_object_class_install_property (object_class, - PROP_CONTEXT, - g_param_spec_object ("context", - "Context", - "MateMixer context", - MATE_MIXER_TYPE_CONTEXT, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_type_class_add_private (klass, sizeof (GvcMixerDialogPrivate)); -} - -static void -gvc_mixer_dialog_init (GvcMixerDialog *dialog) -{ - dialog->priv = GVC_MIXER_DIALOG_GET_PRIVATE (dialog); - - dialog->priv->bars = g_hash_table_new (g_str_hash, g_str_equal); - dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); -} - -static void -gvc_mixer_dialog_finalize (GObject *object) -{ - GvcMixerDialog *dialog; - - dialog = GVC_MIXER_DIALOG (object); - - g_hash_table_destroy (dialog->priv->bars); - - G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->finalize (object); -} - -GvcMixerDialog * -gvc_mixer_dialog_new (MateMixerContext *context) -{ - return g_object_new (GVC_TYPE_MIXER_DIALOG, - "icon-name", "multimedia-volume-control", - "title", _("Sound Preferences"), - "context", context, - NULL); -} - -gboolean -gvc_mixer_dialog_set_page (GvcMixerDialog *self, const gchar *page) -{ - guint num = 0; - - g_return_val_if_fail (GVC_IS_MIXER_DIALOG (self), FALSE); - - if (page != NULL) { - if (g_str_equal (page, "effects")) - num = PAGE_EFFECTS; - else if (g_str_equal (page, "hardware")) - num = PAGE_HARDWARE; - else if (g_str_equal (page, "input")) - num = PAGE_INPUT; - else if (g_str_equal (page, "output")) - num = PAGE_OUTPUT; - else if (g_str_equal (page, "applications")) - num = PAGE_APPLICATIONS; - } - - gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num); - - return TRUE; -} diff --git a/mate-volume-control/src/gvc-mixer-dialog.h b/mate-volume-control/src/gvc-mixer-dialog.h deleted file mode 100644 index 22e522d..0000000 --- a/mate-volume-control/src/gvc-mixer-dialog.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_MIXER_DIALOG_H -#define __GVC_MIXER_DIALOG_H - -#include -#include - -#include - -G_BEGIN_DECLS - -#define GVC_DIALOG_DBUS_NAME "org.mate.VolumeControl" - -#define GVC_TYPE_MIXER_DIALOG (gvc_mixer_dialog_get_type ()) -#define GVC_MIXER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialog)) -#define GVC_MIXER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogClass)) -#define GVC_IS_MIXER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_DIALOG)) -#define GVC_IS_MIXER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_DIALOG)) -#define GVC_MIXER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogClass)) - -typedef struct _GvcMixerDialog GvcMixerDialog; -typedef struct _GvcMixerDialogClass GvcMixerDialogClass; -typedef struct _GvcMixerDialogPrivate GvcMixerDialogPrivate; - -struct _GvcMixerDialog -{ - GtkDialog parent; - GvcMixerDialogPrivate *priv; -}; - -struct _GvcMixerDialogClass -{ - GtkDialogClass parent_class; -}; - -GType gvc_mixer_dialog_get_type (void) G_GNUC_CONST; - -GvcMixerDialog * gvc_mixer_dialog_new (MateMixerContext *context); - -gboolean gvc_mixer_dialog_set_page (GvcMixerDialog *dialog, - const gchar *page); - -G_END_DECLS - -#endif /* __GVC_MIXER_DIALOG_H */ diff --git a/mate-volume-control/src/gvc-speaker-test.c b/mate-volume-control/src/gvc-speaker-test.c deleted file mode 100644 index e638413..0000000 --- a/mate-volume-control/src/gvc-speaker-test.c +++ /dev/null @@ -1,517 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2009 Bastien Nocera - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include - -#include "gvc-speaker-test.h" -#include "gvc-utils.h" - -#define GVC_SPEAKER_TEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestPrivate)) - -struct _GvcSpeakerTestPrivate -{ - GArray *controls; - ca_context *canberra; - MateMixerStream *stream; -}; - -enum { - PROP_0, - PROP_STREAM, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void gvc_speaker_test_class_init (GvcSpeakerTestClass *klass); -static void gvc_speaker_test_init (GvcSpeakerTest *test); -static void gvc_speaker_test_dispose (GObject *object); -static void gvc_speaker_test_finalize (GObject *object); - -#if GTK_CHECK_VERSION (3, 4, 0) -G_DEFINE_TYPE (GvcSpeakerTest, gvc_speaker_test, GTK_TYPE_GRID) -#else -G_DEFINE_TYPE (GvcSpeakerTest, gvc_speaker_test, GTK_TYPE_TABLE) -#endif - -typedef struct { - MateMixerChannelPosition position; - guint left; - guint top; -} TablePosition; - -static const TablePosition positions[] = { - /* Position, X, Y */ - { MATE_MIXER_CHANNEL_FRONT_LEFT, 0, 0, }, - { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, 1, 0, }, - { MATE_MIXER_CHANNEL_FRONT_CENTER, 2, 0, }, - { MATE_MIXER_CHANNEL_MONO, 2, 0, }, - { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, 3, 0, }, - { MATE_MIXER_CHANNEL_FRONT_RIGHT, 4, 0, }, - { MATE_MIXER_CHANNEL_SIDE_LEFT, 0, 1, }, - { MATE_MIXER_CHANNEL_SIDE_RIGHT, 4, 1, }, - { MATE_MIXER_CHANNEL_BACK_LEFT, 0, 2, }, - { MATE_MIXER_CHANNEL_BACK_CENTER, 2, 2, }, - { MATE_MIXER_CHANNEL_BACK_RIGHT, 4, 2, }, - { MATE_MIXER_CHANNEL_LFE, 3, 2 } -}; - -MateMixerStream * -gvc_speaker_test_get_stream (GvcSpeakerTest *test) -{ - g_return_val_if_fail (GVC_IS_SPEAKER_TEST (test), NULL); - - return test->priv->stream; -} - -static void -gvc_speaker_test_set_stream (GvcSpeakerTest *test, MateMixerStream *stream) -{ - MateMixerStreamControl *control; - const gchar *name; - guint i; - - name = mate_mixer_stream_get_name (stream); - control = mate_mixer_stream_get_default_control (stream); - - ca_context_change_device (test->priv->canberra, name); - - for (i = 0; i < G_N_ELEMENTS (positions); i++) { - gboolean has_position = - mate_mixer_stream_control_has_channel_position (control, positions[i].position); - - gtk_widget_set_visible (g_array_index (test->priv->controls, GtkWidget *, i), - has_position); - } - - test->priv->stream = g_object_ref (stream); -} - -static void -gvc_speaker_test_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GvcSpeakerTest *self = GVC_SPEAKER_TEST (object); - - switch (prop_id) { - case PROP_STREAM: - gvc_speaker_test_set_stream (self, g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_speaker_test_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GvcSpeakerTest *self = GVC_SPEAKER_TEST (object); - - switch (prop_id) { - case PROP_STREAM: - g_value_set_object (value, self->priv->stream); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_speaker_test_class_init (GvcSpeakerTestClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gvc_speaker_test_dispose; - object_class->finalize = gvc_speaker_test_finalize; - object_class->set_property = gvc_speaker_test_set_property; - object_class->get_property = gvc_speaker_test_get_property; - - properties[PROP_STREAM] = - g_param_spec_object ("stream", - "Stream", - "MateMixer stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (klass, sizeof (GvcSpeakerTestPrivate)); -} - -static const gchar * -sound_name (MateMixerChannelPosition position) -{ - switch (position) { - case MATE_MIXER_CHANNEL_FRONT_LEFT: - return "audio-channel-front-left"; - case MATE_MIXER_CHANNEL_FRONT_RIGHT: - return "audio-channel-front-right"; - case MATE_MIXER_CHANNEL_FRONT_CENTER: - return "audio-channel-front-center"; - case MATE_MIXER_CHANNEL_BACK_LEFT: - return "audio-channel-rear-left"; - case MATE_MIXER_CHANNEL_BACK_RIGHT: - return "audio-channel-rear-right"; - case MATE_MIXER_CHANNEL_BACK_CENTER: - return "audio-channel-rear-center"; - case MATE_MIXER_CHANNEL_LFE: - return "audio-channel-lfe"; - case MATE_MIXER_CHANNEL_SIDE_LEFT: - return "audio-channel-side-left"; - case MATE_MIXER_CHANNEL_SIDE_RIGHT: - return "audio-channel-side-right"; - default: - return NULL; - } -} - -static const gchar * -icon_name (MateMixerChannelPosition position, gboolean playing) -{ - switch (position) { - case MATE_MIXER_CHANNEL_FRONT_LEFT: - return playing - ? "audio-speaker-left-testing" - : "audio-speaker-left"; - case MATE_MIXER_CHANNEL_FRONT_RIGHT: - return playing - ? "audio-speaker-right-testing" - : "audio-speaker-right"; - case MATE_MIXER_CHANNEL_FRONT_CENTER: - return playing - ? "audio-speaker-center-testing" - : "audio-speaker-center"; - case MATE_MIXER_CHANNEL_BACK_LEFT: - return playing - ? "audio-speaker-left-back-testing" - : "audio-speaker-left-back"; - case MATE_MIXER_CHANNEL_BACK_RIGHT: - return playing - ? "audio-speaker-right-back-testing" - : "audio-speaker-right-back"; - case MATE_MIXER_CHANNEL_BACK_CENTER: - return playing - ? "audio-speaker-center-back-testing" - : "audio-speaker-center-back"; - case MATE_MIXER_CHANNEL_LFE: - return playing - ? "audio-subwoofer-testing" - : "audio-subwoofer"; - case MATE_MIXER_CHANNEL_SIDE_LEFT: - return playing - ? "audio-speaker-left-side-testing" - : "audio-speaker-left-side"; - case MATE_MIXER_CHANNEL_SIDE_RIGHT: - return playing - ? "audio-speaker-right-side-testing" - : "audio-speaker-right-side"; - default: - return NULL; - } -} - -static void -update_button (GtkWidget *control) -{ - GtkWidget *button; - GtkWidget *image; - gboolean playing; - MateMixerChannelPosition position; - - button = g_object_get_data (G_OBJECT (control), "button"); - image = g_object_get_data (G_OBJECT (control), "image"); - - position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position")); - playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing")); - - gtk_button_set_label (GTK_BUTTON (button), playing ? _("Stop") : _("Test")); - - gtk_image_set_from_icon_name (GTK_IMAGE (image), - icon_name (position, playing), - GTK_ICON_SIZE_DIALOG); -} - -static gboolean -idle_cb (GtkWidget *control) -{ - if (control != NULL) { - /* This is called in the background thread, hence forward to main thread - * via idle callback */ - g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); - - update_button (control); - } - return FALSE; -} - -static void -finish_cb (ca_context *c, uint32_t id, int error_code, void *userdata) -{ - GtkWidget *control = (GtkWidget *) userdata; - - if (error_code == CA_ERROR_DESTROYED || control == NULL) - return; - - g_idle_add ((GSourceFunc) idle_cb, control); -} - -static void -on_test_button_clicked (GtkButton *button, GtkWidget *control) -{ - gboolean playing; - ca_context *canberra; - - canberra = g_object_get_data (G_OBJECT (control), "canberra"); - - ca_context_cancel (canberra, 1); - - playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing")); - - if (playing) { - g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); - } else { - MateMixerChannelPosition position; - const gchar *name; - ca_proplist *proplist; - - position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position")); - - ca_proplist_create (&proplist); - ca_proplist_sets (proplist, - CA_PROP_MEDIA_ROLE, "test"); - ca_proplist_sets (proplist, - CA_PROP_MEDIA_NAME, - gvc_channel_position_to_pretty_string (position)); - ca_proplist_sets (proplist, - CA_PROP_CANBERRA_FORCE_CHANNEL, - gvc_channel_position_to_pulse_string (position)); - - ca_proplist_sets (proplist, CA_PROP_CANBERRA_ENABLE, "1"); - - name = sound_name (position); - if (name != NULL) { - ca_proplist_sets (proplist, CA_PROP_EVENT_ID, name); - playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0; - } - - if (!playing) { - ca_proplist_sets (proplist, CA_PROP_EVENT_ID, "audio-test-signal"); - playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0; - } - - if (!playing) { - ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "bell-window-system"); - playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0; - } - - g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (playing)); - } - - update_button (control); -} - -static GtkWidget * -create_control (ca_context *canberra, MateMixerChannelPosition position) -{ - GtkWidget *control; - GtkWidget *box; - GtkWidget *label; - GtkWidget *image; - GtkWidget *test_button; - const gchar *name; - -#if GTK_CHECK_VERSION (3, 0, 0) - control = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); -#else - control = gtk_vbox_new (FALSE, 6); - box = gtk_hbox_new (FALSE, 0); -#endif - g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); - g_object_set_data (G_OBJECT (control), "position", GINT_TO_POINTER (position)); - g_object_set_data (G_OBJECT (control), "canberra", canberra); - - name = icon_name (position, FALSE); - if (name == NULL) - name = "audio-volume-medium"; - - image = gtk_image_new_from_icon_name (name, GTK_ICON_SIZE_DIALOG); - g_object_set_data (G_OBJECT (control), "image", image); - gtk_box_pack_start (GTK_BOX (control), image, FALSE, FALSE, 0); - - label = gtk_label_new (gvc_channel_position_to_pretty_string (position)); - gtk_box_pack_start (GTK_BOX (control), label, FALSE, FALSE, 0); - - test_button = gtk_button_new_with_label (_("Test")); - g_signal_connect (G_OBJECT (test_button), - "clicked", - G_CALLBACK (on_test_button_clicked), - control); - - g_object_set_data (G_OBJECT (control), "button", test_button); - - gtk_box_pack_start (GTK_BOX (box), test_button, TRUE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (control), box, FALSE, FALSE, 0); - - gtk_widget_show_all (control); - - return control; -} - -static void -create_controls (GvcSpeakerTest *test) -{ - guint i; - - for (i = 0; i < G_N_ELEMENTS (positions); i++) { - GtkWidget *control = create_control (test->priv->canberra, positions[i].position); - -#if GTK_CHECK_VERSION (3, 4, 0) - gtk_grid_attach (GTK_GRID (test), - control, - positions[i].left, - positions[i].top, - 1, 1); -#else - gtk_table_attach (GTK_TABLE (test), - control, - positions[i].left, - positions[i].left + 1, - positions[i].top, - positions[i].top + 1, - GTK_EXPAND, GTK_EXPAND, 0, 0); -#endif - g_array_insert_val (test->priv->controls, i, control); - } -} - -static void -gvc_speaker_test_init (GvcSpeakerTest *test) -{ - GtkWidget *face; - - test->priv = GVC_SPEAKER_TEST_GET_PRIVATE (test); - - gtk_container_set_border_width (GTK_CONTAINER (test), 12); - - face = gtk_image_new_from_icon_name ("face-smile", GTK_ICON_SIZE_DIALOG); - -#if GTK_CHECK_VERSION (3, 4, 0) - gtk_grid_attach (GTK_GRID (test), - face, - 1, 1, - 3, 1); - - - gtk_grid_set_baseline_row (GTK_GRID (test), 1); -#else - gtk_table_attach (GTK_TABLE (test), - face, - 2, 3, 1, 2, - GTK_EXPAND, - GTK_EXPAND, - 0, 0); -#endif - gtk_widget_show (face); - - ca_context_create (&test->priv->canberra); - - /* The test sounds are played for a single channel, set up using the - * FORCE_CHANNEL property of libcanberra; this property is only supported - * in the PulseAudio backend, so avoid other backends completely */ - ca_context_set_driver (test->priv->canberra, "pulse"); - - ca_context_change_props (test->priv->canberra, - CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", - CA_PROP_APPLICATION_NAME, _("Volume Control"), - CA_PROP_APPLICATION_VERSION, VERSION, - CA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control", - NULL); - - test->priv->controls = g_array_new (FALSE, FALSE, sizeof (GtkWidget *)); - - create_controls (test); -} - -static void -gvc_speaker_test_dispose (GObject *object) -{ - GvcSpeakerTest *test; - - test = GVC_SPEAKER_TEST (object); - - g_clear_object (&test->priv->stream); - - G_OBJECT_CLASS (gvc_speaker_test_parent_class)->dispose (object); -} - -static void -gvc_speaker_test_finalize (GObject *object) -{ - GvcSpeakerTest *test; - - test = GVC_SPEAKER_TEST (object); - - ca_context_destroy (test->priv->canberra); - - G_OBJECT_CLASS (gvc_speaker_test_parent_class)->finalize (object); -} - -GtkWidget * -gvc_speaker_test_new (MateMixerStream *stream) -{ - GObject *test; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - - test = g_object_new (GVC_TYPE_SPEAKER_TEST, - "row-spacing", 6, - "column-spacing", 6, -#if GTK_CHECK_VERSION (3, 4, 0) - "row-homogeneous", TRUE, - "column-homogeneous", TRUE, -#else - "homogeneous", TRUE, - "n-rows", 3, - "n-columns", 5, -#endif - "stream", stream, - NULL); - - return GTK_WIDGET (test); -} diff --git a/mate-volume-control/src/gvc-speaker-test.h b/mate-volume-control/src/gvc-speaker-test.h deleted file mode 100644 index 1c1546d..0000000 --- a/mate-volume-control/src/gvc-speaker-test.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_SPEAKER_TEST_H -#define __GVC_SPEAKER_TEST_H - -#include -#include -#include - -#include - -G_BEGIN_DECLS - -#define GVC_TYPE_SPEAKER_TEST (gvc_speaker_test_get_type ()) -#define GVC_SPEAKER_TEST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTest)) -#define GVC_SPEAKER_TEST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestClass)) -#define GVC_IS_SPEAKER_TEST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_SPEAKER_TEST)) -#define GVC_IS_SPEAKER_TEST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_SPEAKER_TEST)) -#define GVC_SPEAKER_TEST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestClass)) - -typedef struct _GvcSpeakerTest GvcSpeakerTest; -typedef struct _GvcSpeakerTestClass GvcSpeakerTestClass; -typedef struct _GvcSpeakerTestPrivate GvcSpeakerTestPrivate; - -struct _GvcSpeakerTest -{ -#if GTK_CHECK_VERSION (3, 4, 0) - GtkGrid parent; -#else - GtkTable parent; -#endif - GvcSpeakerTestPrivate *priv; -}; - -struct _GvcSpeakerTestClass -{ -#if GTK_CHECK_VERSION (3, 4, 0) - GtkGridClass parent_class; -#else - GtkTableClass parent_class; -#endif -}; - -GType gvc_speaker_test_get_type (void) G_GNUC_CONST; - -GtkWidget * gvc_speaker_test_new (MateMixerStream *stream); - -MateMixerStream * gvc_speaker_test_get_stream (GvcSpeakerTest *test); - -G_END_DECLS - -#endif /* __GVC_SPEAKER_TEST_H */ diff --git a/mate-volume-control/src/gvc-stream-status-icon.c b/mate-volume-control/src/gvc-stream-status-icon.c deleted file mode 100644 index 3ede4b1..0000000 --- a/mate-volume-control/src/gvc-stream-status-icon.c +++ /dev/null @@ -1,805 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 William Jon McCann - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include - -#include - -#define MATE_DESKTOP_USE_UNSTABLE_API -#include - -#include "gvc-channel-bar.h" -#include "gvc-stream-status-icon.h" - -#define GVC_STREAM_STATUS_ICON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIconPrivate)) - -struct _GvcStreamStatusIconPrivate -{ - gchar **icon_names; - GtkWidget *dock; - GtkWidget *bar; - guint current_icon; - gchar *display_name; - MateMixerStreamControl *control; -}; - -enum -{ - PROP_0, - PROP_CONTROL, - PROP_DISPLAY_NAME, - PROP_ICON_NAMES, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass); -static void gvc_stream_status_icon_init (GvcStreamStatusIcon *stream_status_icon); -static void gvc_stream_status_icon_finalize (GObject *object); - -G_DEFINE_TYPE (GvcStreamStatusIcon, gvc_stream_status_icon, GTK_TYPE_STATUS_ICON) - -static gboolean -popup_dock (GvcStreamStatusIcon *icon, guint time) -{ - GdkRectangle area; - GtkOrientation orientation; - GdkDisplay *display; - GdkScreen *screen; - int x; - int y; - int monitor_num; - GdkRectangle monitor; - GtkRequisition dock_req; - - screen = gtk_status_icon_get_screen (GTK_STATUS_ICON (icon)); - - if (gtk_status_icon_get_geometry (GTK_STATUS_ICON (icon), - &screen, - &area, - &orientation) == FALSE) { - g_warning ("Unable to determine geometry of status icon"); - return FALSE; - } - - /* position roughly */ - gtk_window_set_screen (GTK_WINDOW (icon->priv->dock), screen); - gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar), - 1 - orientation); - - monitor_num = gdk_screen_get_monitor_at_point (screen, area.x, area.y); - gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); - - gtk_container_foreach (GTK_CONTAINER (icon->priv->dock), - (GtkCallback) gtk_widget_show_all, NULL); -#if GTK_CHECK_VERSION (3, 0, 0) - gtk_widget_get_preferred_size (icon->priv->dock, &dock_req, NULL); -#else - gtk_widget_size_request (icon->priv->dock, &dock_req); -#endif - - if (orientation == GTK_ORIENTATION_VERTICAL) { - if (area.x + area.width + dock_req.width <= monitor.x + monitor.width) - x = area.x + area.width; - else - x = area.x - dock_req.width; - - if (area.y + dock_req.height <= monitor.y + monitor.height) - y = area.y; - else - y = monitor.y + monitor.height - dock_req.height; - } else { - if (area.y + area.height + dock_req.height <= monitor.y + monitor.height) - y = area.y + area.height; - else - y = area.y - dock_req.height; - - if (area.x + dock_req.width <= monitor.x + monitor.width) - x = area.x; - else - x = monitor.x + monitor.width - dock_req.width; - } - - gtk_window_move (GTK_WINDOW (icon->priv->dock), x, y); - - /* Without this, the popup window appears as a square after changing - * the orientation */ - gtk_window_resize (GTK_WINDOW (icon->priv->dock), 1, 1); - - gtk_widget_show_all (icon->priv->dock); - - /* Grab focus */ - gtk_grab_add (icon->priv->dock); - - display = gtk_widget_get_display (icon->priv->dock); - -#if GTK_CHECK_VERSION (3, 0, 0) - do { - GdkDeviceManager *manager = gdk_display_get_device_manager (display); - - if (gdk_device_grab (gdk_device_manager_get_client_pointer (manager), - gtk_widget_get_window (icon->priv->dock), - GDK_OWNERSHIP_NONE, - TRUE, - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_SCROLL_MASK, - NULL, - time) != GDK_GRAB_SUCCESS) { - gtk_grab_remove (icon->priv->dock); - gtk_widget_hide (icon->priv->dock); - } - } while (0); -#else - if (gdk_pointer_grab (gtk_widget_get_window (icon->priv->dock), - TRUE, - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_SCROLL_MASK, - NULL, NULL, - time) != GDK_GRAB_SUCCESS) { - gtk_grab_remove (icon->priv->dock); - gtk_widget_hide (icon->priv->dock); - return FALSE; - } - - if (gdk_keyboard_grab (gtk_widget_get_window (icon->priv->dock), - TRUE, - time) != GDK_GRAB_SUCCESS) { - gdk_display_pointer_ungrab (display, time); - gtk_grab_remove (icon->priv->dock); - gtk_widget_hide (icon->priv->dock); - return FALSE; - } -#endif - gtk_widget_grab_focus (icon->priv->dock); - - return TRUE; -} - -static void -on_status_icon_activate (GtkStatusIcon *status_icon, GvcStreamStatusIcon *icon) -{ - popup_dock (icon, GDK_CURRENT_TIME); -} - -static gboolean -on_status_icon_button_press (GtkStatusIcon *status_icon, - GdkEventButton *event, - GvcStreamStatusIcon *icon) -{ - /* Middle click acts as mute/unmute */ - if (event->button == 2) { - gboolean is_muted = mate_mixer_stream_control_get_mute (icon->priv->control); - - mate_mixer_stream_control_set_mute (icon->priv->control, !is_muted); - return TRUE; - } - return FALSE; -} - -static void -on_menu_mute_toggled (GtkMenuItem *item, GvcStreamStatusIcon *icon) -{ - gboolean is_muted; - - is_muted = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)); - - mate_mixer_stream_control_set_mute (icon->priv->control, is_muted); -} - -static void -on_menu_activate_open_volume_control (GtkMenuItem *item, - GvcStreamStatusIcon *icon) -{ - GError *error = NULL; - - mate_gdk_spawn_command_line_on_screen (gtk_widget_get_screen (icon->priv->dock), - "mate-volume-control", - &error); - - if (error != NULL) { - GtkWidget *dialog; - - dialog = gtk_message_dialog_new (NULL, - 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("Failed to start Sound Preferences: %s"), - error->message); - g_signal_connect (G_OBJECT (dialog), - "response", - G_CALLBACK (gtk_widget_destroy), - NULL); - gtk_widget_show (dialog); - g_error_free (error); - } -} - -static void -on_status_icon_popup_menu (GtkStatusIcon *status_icon, - guint button, - guint activate_time, - GvcStreamStatusIcon *icon) -{ - GtkWidget *menu; - GtkWidget *item; - - menu = gtk_menu_new (); - item = gtk_check_menu_item_new_with_mnemonic (_("_Mute")); - - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), - mate_mixer_stream_control_get_mute (icon->priv->control)); - g_signal_connect (G_OBJECT (item), - "toggled", - G_CALLBACK (on_menu_mute_toggled), - icon); - - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - -#if GTK_CHECK_VERSION (3, 10, 0) - item = gtk_menu_item_new_with_mnemonic (_("_Sound Preferences")); -#else - item = gtk_image_menu_item_new_with_mnemonic (_("_Sound Preferences")); - - do { - GtkWidget *image; - - image = gtk_image_new_from_icon_name ("multimedia-volume-control", - GTK_ICON_SIZE_MENU); - - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); - } while (0); -#endif - g_signal_connect (G_OBJECT (item), - "activate", - G_CALLBACK (on_menu_activate_open_volume_control), - icon); - - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - - gtk_widget_show_all (menu); - gtk_menu_popup (GTK_MENU (menu), - NULL, - NULL, - gtk_status_icon_position_menu, - status_icon, - button, - activate_time); -} - -static gboolean -on_status_icon_scroll_event (GtkStatusIcon *status_icon, - GdkEventScroll *event, - GvcStreamStatusIcon *icon) -{ - return gvc_channel_bar_scroll (GVC_CHANNEL_BAR (icon->priv->bar), event->direction); -} - -static void -gvc_icon_release_grab (GvcStreamStatusIcon *icon, GdkEventButton *event) -{ -#if GTK_CHECK_VERSION (3, 0, 0) - gdk_device_ungrab (event->device, event->time); -#else - GdkDisplay *display; - - display = gtk_widget_get_display (GTK_WIDGET (icon->priv->dock)); - - gdk_display_keyboard_ungrab (display, event->time); - gdk_display_pointer_ungrab (display, event->time); -#endif - gtk_grab_remove (icon->priv->dock); - - /* Hide again */ - gtk_widget_hide (icon->priv->dock); -} - -static gboolean -on_dock_button_press (GtkWidget *widget, - GdkEventButton *event, - GvcStreamStatusIcon *icon) -{ - if (event->type == GDK_BUTTON_PRESS) { - gvc_icon_release_grab (icon, event); - return TRUE; - } - - return FALSE; -} - -static void -popdown_dock (GvcStreamStatusIcon *icon) -{ - GdkDisplay *display; - - display = gtk_widget_get_display (icon->priv->dock); - -#if GTK_CHECK_VERSION (3, 0, 0) - GdkDeviceManager *manager = gdk_display_get_device_manager (display); - - gdk_device_ungrab (gdk_device_manager_get_client_pointer (manager), - GDK_CURRENT_TIME); -#else - gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME); - gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); - gtk_grab_remove (icon->priv->dock); -#endif - /* Hide again */ - gtk_widget_hide (icon->priv->dock); -} - -/* This is called when the grab is broken for either the dock, or the scale */ -static void -gvc_icon_grab_notify (GvcStreamStatusIcon *icon, gboolean was_grabbed) -{ - if (was_grabbed != FALSE) - return; - - if (gtk_widget_has_grab (icon->priv->dock) == FALSE) - return; - - if (gtk_widget_is_ancestor (gtk_grab_get_current (), icon->priv->dock)) - return; - - popdown_dock (icon); -} - -static void -on_dock_grab_notify (GtkWidget *widget, - gboolean was_grabbed, - GvcStreamStatusIcon *icon) -{ - gvc_icon_grab_notify (icon, was_grabbed); -} - -static gboolean -on_dock_grab_broken_event (GtkWidget *widget, - gboolean was_grabbed, - GvcStreamStatusIcon *icon) -{ - gvc_icon_grab_notify (icon, FALSE); - return FALSE; -} - -static gboolean -on_dock_key_release (GtkWidget *widget, - GdkEventKey *event, - GvcStreamStatusIcon *icon) -{ - if (event->keyval == GDK_KEY_Escape) { - popdown_dock (icon); - return TRUE; - } - return TRUE; -} - -static gboolean -on_dock_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcStreamStatusIcon *icon) -{ - /* Forward event to the status icon */ - on_status_icon_scroll_event (NULL, event, icon); - return TRUE; -} - -static void -update_icon (GvcStreamStatusIcon *icon) -{ - guint volume = 0; - gdouble decibel = 0; - guint normal = 0; - gboolean muted = FALSE; - guint n = 0; - gchar *markup; - const gchar *description; - MateMixerStreamControlFlags flags; - - if (icon->priv->control == NULL) { - /* Do not bother creating a tooltip for an unusable icon as it - * has no practical use */ - gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), FALSE); - return; - } else - gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), TRUE); - - flags = mate_mixer_stream_control_get_flags (icon->priv->control); - - if (flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE) - muted = mate_mixer_stream_control_get_mute (icon->priv->control); - - if (flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { - volume = mate_mixer_stream_control_get_volume (icon->priv->control); - normal = mate_mixer_stream_control_get_normal_volume (icon->priv->control); - - /* Select an icon, they are expected to be sorted, the lowest index being - * the mute icon and the rest being volume increments */ - if (volume <= 0 || muted) - n = 0; - else - n = CLAMP (3 * volume / normal + 1, 1, 3); - } - if (flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) - decibel = mate_mixer_stream_control_get_decibel (icon->priv->control); - - /* Apparently status icon will reset icon even if it doesn't change */ - if (icon->priv->current_icon != n) { - gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), - icon->priv->icon_names[n]); - icon->priv->current_icon = n; - } - - description = mate_mixer_stream_control_get_label (icon->priv->control); - - if (muted) { - markup = g_strdup_printf ("%s: %s\n%s", - icon->priv->display_name, - _("Muted"), - description); - } else if (flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { - gdouble display_volume = 100 * volume / normal; - - if (flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) { - if (decibel > -MATE_MIXER_INFINITY) { - markup = g_strdup_printf ("%s: %.0f%%\n" - "%0.2f dB\n%s", - icon->priv->display_name, - display_volume, - decibel, - description); - } else { - markup = g_strdup_printf ("%s: %.0f%%\n" - "-∞ dB\n%s", - icon->priv->display_name, - display_volume, - description); - } - } else { - markup = g_strdup_printf ("%s: %.0f%%\n%s", - icon->priv->display_name, - display_volume, - description); - } - } else { - markup = g_strdup_printf ("%s\n%s", - icon->priv->display_name, - description); - } - - gtk_status_icon_set_tooltip_markup (GTK_STATUS_ICON (icon), markup); - - g_free (markup); -} - -void -gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon *icon, - const gchar **names) -{ - g_return_if_fail (GVC_IS_STREAM_STATUS_ICON (icon)); - g_return_if_fail (names != NULL && *names != NULL); - - if (G_UNLIKELY (g_strv_length ((gchar **) names) != 4)) { - g_warn_if_reached (); - return; - } - - g_strfreev (icon->priv->icon_names); - - icon->priv->icon_names = g_strdupv ((gchar **) names); - - /* Set the first icon as the initial one, the icon may be immediately - * updated or not depending on whether a stream is available */ - gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), names[0]); - update_icon (icon); - - g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_ICON_NAMES]); -} - -static void -on_stream_control_volume_notify (MateMixerStreamControl *control, - GParamSpec *pspec, - GvcStreamStatusIcon *icon) -{ - update_icon (icon); -} - -static void -on_stream_control_mute_notify (MateMixerStreamControl *control, - GParamSpec *pspec, - GvcStreamStatusIcon *icon) -{ - update_icon (icon); -} - -void -gvc_stream_status_icon_set_display_name (GvcStreamStatusIcon *icon, - const gchar *name) -{ - g_return_if_fail (GVC_STREAM_STATUS_ICON (icon)); - - g_free (icon->priv->display_name); - - icon->priv->display_name = g_strdup (name); - update_icon (icon); - - g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_DISPLAY_NAME]); -} - -void -gvc_stream_status_icon_set_control (GvcStreamStatusIcon *icon, - MateMixerStreamControl *control) -{ - g_return_if_fail (GVC_STREAM_STATUS_ICON (icon)); - - if (icon->priv->control == control) - return; - - if (control != NULL) - g_object_ref (control); - - if (icon->priv->control != NULL) { - g_signal_handlers_disconnect_by_func (G_OBJECT (icon->priv->control), - G_CALLBACK (on_stream_control_volume_notify), - icon); - g_signal_handlers_disconnect_by_func (G_OBJECT (icon->priv->control), - G_CALLBACK (on_stream_control_mute_notify), - icon); - - g_object_unref (icon->priv->control); - } - - icon->priv->control = control; - - if (icon->priv->control != NULL) { - g_signal_connect (G_OBJECT (icon->priv->control), - "notify::volume", - G_CALLBACK (on_stream_control_volume_notify), - icon); - g_signal_connect (G_OBJECT (icon->priv->control), - "notify::mute", - G_CALLBACK (on_stream_control_mute_notify), - icon); - - // XXX when no stream set some default icon and "unset" dock - update_icon (icon); - } - - gvc_channel_bar_set_control (GVC_CHANNEL_BAR (icon->priv->bar), icon->priv->control); - - g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_CONTROL]); -} - -static void -gvc_stream_status_icon_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object); - - switch (prop_id) { - case PROP_CONTROL: - gvc_stream_status_icon_set_control (self, g_value_get_object (value)); - break; - case PROP_DISPLAY_NAME: - gvc_stream_status_icon_set_display_name (self, g_value_get_string (value)); - break; - case PROP_ICON_NAMES: - gvc_stream_status_icon_set_icon_names (self, g_value_get_boxed (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_stream_status_icon_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object); - - switch (prop_id) { - case PROP_CONTROL: - g_value_set_object (value, self->priv->control); - break; - case PROP_DISPLAY_NAME: - g_value_set_string (value, self->priv->display_name); - break; - case PROP_ICON_NAMES: - g_value_set_boxed (value, self->priv->icon_names); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gvc_stream_status_icon_dispose (GObject *object) -{ - GvcStreamStatusIcon *icon = GVC_STREAM_STATUS_ICON (object); - - if (icon->priv->dock != NULL) { - gtk_widget_destroy (icon->priv->dock); - icon->priv->dock = NULL; - } - - g_clear_object (&icon->priv->control); - - G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->dispose (object); -} - -static void -gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = gvc_stream_status_icon_finalize; - object_class->dispose = gvc_stream_status_icon_dispose; - object_class->set_property = gvc_stream_status_icon_set_property; - object_class->get_property = gvc_stream_status_icon_get_property; - - properties[PROP_CONTROL] = - g_param_spec_object ("control", - "Control", - "MateMixer stream control", - MATE_MIXER_TYPE_STREAM_CONTROL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_DISPLAY_NAME] = - g_param_spec_string ("display-name", - "Display name", - "Name to display for this stream", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - properties[PROP_ICON_NAMES] = - g_param_spec_boxed ("icon-names", - "Icon names", - "Name of icon to display for this stream", - G_TYPE_STRV, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (klass, sizeof (GvcStreamStatusIconPrivate)); -} - -static void -on_status_icon_visible_notify (GvcStreamStatusIcon *icon) -{ - if (gtk_status_icon_get_visible (GTK_STATUS_ICON (icon)) == FALSE) - gtk_widget_hide (icon->priv->dock); -} - -static void -gvc_stream_status_icon_init (GvcStreamStatusIcon *icon) -{ - GtkWidget *frame; - GtkWidget *box; - - icon->priv = GVC_STREAM_STATUS_ICON_GET_PRIVATE (icon); - - g_signal_connect (G_OBJECT (icon), - "activate", - G_CALLBACK (on_status_icon_activate), - icon); - g_signal_connect (G_OBJECT (icon), - "button-press-event", - G_CALLBACK (on_status_icon_button_press), - icon); - g_signal_connect (G_OBJECT (icon), - "popup-menu", - G_CALLBACK (on_status_icon_popup_menu), - icon); - g_signal_connect (G_OBJECT (icon), - "scroll-event", - G_CALLBACK (on_status_icon_scroll_event), - icon); - g_signal_connect (G_OBJECT (icon), - "notify::visible", - G_CALLBACK (on_status_icon_visible_notify), - NULL); - - /* Create the dock window */ - icon->priv->dock = gtk_window_new (GTK_WINDOW_POPUP); - - gtk_window_set_decorated (GTK_WINDOW (icon->priv->dock), FALSE); - - g_signal_connect (G_OBJECT (icon->priv->dock), - "button-press-event", - G_CALLBACK (on_dock_button_press), - icon); - g_signal_connect (G_OBJECT (icon->priv->dock), - "key-release-event", - G_CALLBACK (on_dock_key_release), - icon); - g_signal_connect (G_OBJECT (icon->priv->dock), - "scroll-event", - G_CALLBACK (on_dock_scroll_event), - icon); - g_signal_connect (G_OBJECT (icon->priv->dock), - "grab-notify", - G_CALLBACK (on_dock_grab_notify), - icon); - g_signal_connect (G_OBJECT (icon->priv->dock), - "grab-broken-event", - G_CALLBACK (on_dock_grab_broken_event), - icon); - - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); - gtk_container_add (GTK_CONTAINER (icon->priv->dock), frame); - - icon->priv->bar = gvc_channel_bar_new (NULL); - - gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar), - GTK_ORIENTATION_VERTICAL); - -#if GTK_CHECK_VERSION (3, 0, 0) - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); -#else - box = gtk_vbox_new (FALSE, 6); -#endif - - gtk_container_set_border_width (GTK_CONTAINER (box), 2); - gtk_container_add (GTK_CONTAINER (frame), box); - - gtk_box_pack_start (GTK_BOX (box), icon->priv->bar, TRUE, FALSE, 0); -} - -static void -gvc_stream_status_icon_finalize (GObject *object) -{ - GvcStreamStatusIcon *icon; - - icon = GVC_STREAM_STATUS_ICON (object); - - g_strfreev (icon->priv->icon_names); - - G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->finalize (object); -} - -GvcStreamStatusIcon * -gvc_stream_status_icon_new (MateMixerStreamControl *control, - const gchar **icon_names) -{ - return g_object_new (GVC_TYPE_STREAM_STATUS_ICON, - "control", control, - "icon-names", icon_names, - NULL); -} diff --git a/mate-volume-control/src/gvc-stream-status-icon.h b/mate-volume-control/src/gvc-stream-status-icon.h deleted file mode 100644 index 7b51801..0000000 --- a/mate-volume-control/src/gvc-stream-status-icon.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_STREAM_STATUS_ICON_H -#define __GVC_STREAM_STATUS_ICON_H - -#include -#include -#include - -G_BEGIN_DECLS - -#define GVC_TYPE_STREAM_STATUS_ICON (gvc_stream_status_icon_get_type ()) -#define GVC_STREAM_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIcon)) -#define GVC_STREAM_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIconClass)) -#define GVC_IS_STREAM_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_STREAM_STATUS_ICON)) -#define GVC_IS_STREAM_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_STREAM_STATUS_ICON)) -#define GVC_STREAM_STATUS_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIconClass)) - -typedef struct _GvcStreamStatusIcon GvcStreamStatusIcon; -typedef struct _GvcStreamStatusIconClass GvcStreamStatusIconClass; -typedef struct _GvcStreamStatusIconPrivate GvcStreamStatusIconPrivate; - -struct _GvcStreamStatusIcon -{ - GtkStatusIcon parent; - GvcStreamStatusIconPrivate *priv; -}; - -struct _GvcStreamStatusIconClass -{ - GtkStatusIconClass parent_class; -}; - -GType gvc_stream_status_icon_get_type (void) G_GNUC_CONST; - -GvcStreamStatusIcon * gvc_stream_status_icon_new (MateMixerStreamControl *control, - const gchar **icon_names); - -void gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon *icon, - const gchar **icon_names); -void gvc_stream_status_icon_set_display_name (GvcStreamStatusIcon *icon, - const gchar *display_name); - -void gvc_stream_status_icon_set_control (GvcStreamStatusIcon *icon, - MateMixerStreamControl *control); - -G_END_DECLS - -#endif /* __GVC_STREAM_STATUS_ICON_H */ diff --git a/mate-volume-control/src/gvc-utils.c b/mate-volume-control/src/gvc-utils.c deleted file mode 100644 index d45a217..0000000 --- a/mate-volume-control/src/gvc-utils.c +++ /dev/null @@ -1,335 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" - -#include -#include -#include - -#include - -#include "gvc-utils.h" - -/* libcanberra requires a PulseAudio channel name to be given to its - * CA_PROP_CANBERRA_FORCE_CHANNEL property. - * - * The strings here are copied from PulseAudio source code to avoid depending - * on libpulse. */ -static const gchar *pulse_position[MATE_MIXER_CHANNEL_MAX] = { - [MATE_MIXER_CHANNEL_MONO] = "mono", - [MATE_MIXER_CHANNEL_FRONT_LEFT] = "front-left", - [MATE_MIXER_CHANNEL_FRONT_RIGHT] = "front-right", - [MATE_MIXER_CHANNEL_FRONT_CENTER] = "front-center", - [MATE_MIXER_CHANNEL_LFE] = "lfe", - [MATE_MIXER_CHANNEL_BACK_LEFT] = "rear-left", - [MATE_MIXER_CHANNEL_BACK_RIGHT] = "rear-right", - [MATE_MIXER_CHANNEL_BACK_CENTER] = "rear-center", - [MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER] = "front-left-of-center", - [MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER] = "front-right-of-center", - [MATE_MIXER_CHANNEL_SIDE_LEFT] = "side-left", - [MATE_MIXER_CHANNEL_SIDE_RIGHT] = "side-right", - [MATE_MIXER_CHANNEL_TOP_FRONT_LEFT] = "top-front-left", - [MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT] = "top-front-right", - [MATE_MIXER_CHANNEL_TOP_FRONT_CENTER] = "top-front-center", - [MATE_MIXER_CHANNEL_TOP_CENTER] = "top-center", - [MATE_MIXER_CHANNEL_TOP_BACK_LEFT] = "top-rear-left", - [MATE_MIXER_CHANNEL_TOP_BACK_RIGHT] = "top-rear-right", - [MATE_MIXER_CHANNEL_TOP_BACK_CENTER] = "top-rear-center" -}; - -static const gchar *pretty_position[MATE_MIXER_CHANNEL_MAX] = { - [MATE_MIXER_CHANNEL_UNKNOWN] = N_("Unknown"), - /* Speaker channel names */ - [MATE_MIXER_CHANNEL_MONO] = N_("Mono"), - [MATE_MIXER_CHANNEL_FRONT_LEFT] = N_("Front Left"), - [MATE_MIXER_CHANNEL_FRONT_RIGHT] = N_("Front Right"), - [MATE_MIXER_CHANNEL_FRONT_CENTER] = N_("Front Center"), - [MATE_MIXER_CHANNEL_LFE] = N_("LFE"), - [MATE_MIXER_CHANNEL_BACK_LEFT] = N_("Rear Left"), - [MATE_MIXER_CHANNEL_BACK_RIGHT] = N_("Rear Right"), - [MATE_MIXER_CHANNEL_BACK_CENTER] = N_("Rear Center"), - [MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER] = N_("Front Left of Center"), - [MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER] = N_("Front Right of Center"), - [MATE_MIXER_CHANNEL_SIDE_LEFT] = N_("Side Left"), - [MATE_MIXER_CHANNEL_SIDE_RIGHT] = N_("Side Right"), - [MATE_MIXER_CHANNEL_TOP_FRONT_LEFT] = N_("Top Front Left"), - [MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT] = N_("Top Front Right"), - [MATE_MIXER_CHANNEL_TOP_FRONT_CENTER] = N_("Top Front Center"), - [MATE_MIXER_CHANNEL_TOP_CENTER] = N_("Top Center"), - [MATE_MIXER_CHANNEL_TOP_BACK_LEFT] = N_("Top Rear Left"), - [MATE_MIXER_CHANNEL_TOP_BACK_RIGHT] = N_("Top Rear Right"), - [MATE_MIXER_CHANNEL_TOP_BACK_CENTER] = N_("Top Rear Center") -}; - -const gchar * -gvc_channel_position_to_pulse_string (MateMixerChannelPosition position) -{ - g_return_val_if_fail (position >= 0 && position < MATE_MIXER_CHANNEL_MAX, NULL); - - return pulse_position[position]; -} - -const gchar * -gvc_channel_position_to_pretty_string (MateMixerChannelPosition position) -{ - g_return_val_if_fail (position >= 0 && position < MATE_MIXER_CHANNEL_MAX, NULL); - - return pretty_position[position]; -} - -const gchar * -gvc_channel_map_to_pretty_string (MateMixerStreamControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), NULL); - -#define HAS_POSITION(p) (mate_mixer_stream_control_has_channel_position (control, (p))) - - /* Modeled after PulseAudio 5.0, probably could be extended with other combinations */ - switch (mate_mixer_stream_control_get_num_channels (control)) { - case 1: - if (HAS_POSITION (MATE_MIXER_CHANNEL_MONO)) - return _("Mono"); - break; - case 2: - if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT)) - return _("Stereo"); - break; - case 4: - if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT)) - return _("Surround 4.0"); - break; - case 5: - if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT)) - if (HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) - return _("Surround 4.1"); - if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER)) - return _("Surround 5.0"); - break; - case 6: - if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && - HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT) && - HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) - return _("Surround 5.1"); - break; - case 8: - if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && - HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT) && - HAS_POSITION (MATE_MIXER_CHANNEL_SIDE_LEFT) && - HAS_POSITION (MATE_MIXER_CHANNEL_SIDE_RIGHT) && - HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) - return _("Surround 7.1"); - break; - } - -#undef HAS_POSITION - - return NULL; -} - -#if GTK_CHECK_VERSION (3, 0, 0) -/* Taken from gtkstyle.c */ -static void rgb_to_hls (gdouble *r, gdouble *g, gdouble *b); -static void hls_to_rgb (gdouble *h, gdouble *l, gdouble *s); - -void -gvc_color_shade (GdkRGBA *a, GdkRGBA *b, gdouble k) -{ - gdouble red; - gdouble green; - gdouble blue; - - red = (gdouble) a->red / 65535.0; - green = (gdouble) a->green / 65535.0; - blue = (gdouble) a->blue / 65535.0; - - rgb_to_hls (&red, &green, &blue); - - green *= k; - if (green > 1.0) - green = 1.0; - else if (green < 0.0) - green = 0.0; - - blue *= k; - if (blue > 1.0) - blue = 1.0; - else if (blue < 0.0) - blue = 0.0; - - hls_to_rgb (&red, &green, &blue); - - b->red = red * 65535.0; - b->green = green * 65535.0; - b->blue = blue * 65535.0; -} - -static void -rgb_to_hls (gdouble *r, gdouble *g, gdouble *b) -{ - gdouble min; - gdouble max; - gdouble red; - gdouble green; - gdouble blue; - gdouble h, l, s; - gdouble delta; - - red = *r; - green = *g; - blue = *b; - - if (red > green) { - if (red > blue) - max = red; - else - max = blue; - - if (green < blue) - min = green; - else - min = blue; - } else { - if (green > blue) - max = green; - else - max = blue; - - if (red < blue) - min = red; - else - min = blue; - } - - l = (max + min) / 2; - s = 0; - h = 0; - - if (max != min) { - if (l <= 0.5) - s = (max - min) / (max + min); - else - s = (max - min) / (2 - max - min); - - delta = max - min; - if (red == max) - h = (green - blue) / delta; - else if (green == max) - h = 2 + (blue - red) / delta; - else if (blue == max) - h = 4 + (red - green) / delta; - - h *= 60; - if (h < 0.0) - h += 360; - } - - *r = h; - *g = l; - *b = s; -} - -static void -hls_to_rgb (gdouble *h, gdouble *l, gdouble *s) -{ - gdouble hue; - gdouble lightness; - gdouble saturation; - gdouble m1, m2; - gdouble r, g, b; - - lightness = *l; - saturation = *s; - - if (lightness <= 0.5) - m2 = lightness * (1 + saturation); - else - m2 = lightness + saturation - lightness * saturation; - m1 = 2 * lightness - m2; - - if (saturation == 0) { - *h = lightness; - *l = lightness; - *s = lightness; - } else { - hue = *h + 120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - r = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - r = m2; - else if (hue < 240) - r = m1 + (m2 - m1) * (240 - hue) / 60; - else - r = m1; - - hue = *h; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - g = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - g = m2; - else if (hue < 240) - g = m1 + (m2 - m1) * (240 - hue) / 60; - else - g = m1; - - hue = *h - 120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - b = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - b = m2; - else if (hue < 240) - b = m1 + (m2 - m1) * (240 - hue) / 60; - else - b = m1; - - *h = r; - *l = g; - *s = b; - } -} -#endif diff --git a/mate-volume-control/src/gvc-utils.h b/mate-volume-control/src/gvc-utils.h deleted file mode 100644 index 4eca7a7..0000000 --- a/mate-volume-control/src/gvc-utils.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2014 Michal Ratajsky - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef __GVC_HELPERS_H -#define __GVC_HELPERS_H - -#include - -#include - -G_BEGIN_DECLS - -const gchar *gvc_channel_position_to_pulse_string (MateMixerChannelPosition position); -const gchar *gvc_channel_position_to_pretty_string (MateMixerChannelPosition position); -const gchar *gvc_channel_map_to_pretty_string (MateMixerStreamControl *control); - -#if GTK_CHECK_VERSION (3, 0, 0) -void gvc_color_shade (GdkRGBA *a, - GdkRGBA *b, - gdouble k); -#endif - -G_END_DECLS - -#endif /* __GVC_HELPERS_H */ -- cgit v1.2.1