From 11ea05685763cd7a233b56e11cd231c7774f69e1 Mon Sep 17 00:00:00 2001 From: Robert Tari Date: Tue, 11 Aug 2020 12:20:04 +0200 Subject: Replace x-canonical attributes --- src/gmenuharness/MenuItemMatcher.cpp | 2 +- src/info-notification.vala | 172 +++---- src/sound-menu.vala | 904 +++++++++++++++++------------------ src/warn-notification.vala | 60 +-- 4 files changed, 569 insertions(+), 569 deletions(-) (limited to 'src') diff --git a/src/gmenuharness/MenuItemMatcher.cpp b/src/gmenuharness/MenuItemMatcher.cpp index f39acef..f2d5fe8 100644 --- a/src/gmenuharness/MenuItemMatcher.cpp +++ b/src/gmenuharness/MenuItemMatcher.cpp @@ -306,7 +306,7 @@ MenuItemMatcher& MenuItemMatcher::themed_icon(const std::string& iconName, const MenuItemMatcher& MenuItemMatcher::widget(const string& widget) { - return string_attribute("x-canonical-type", widget); + return string_attribute("x-ayatana-type", widget); } MenuItemMatcher& MenuItemMatcher::pass_through_attribute(const string& actionName, const shared_ptr& value) diff --git a/src/info-notification.vala b/src/info-notification.vala index 84987dc..e093aae 100644 --- a/src/info-notification.vala +++ b/src/info-notification.vala @@ -21,102 +21,102 @@ using Notify; public class IndicatorSound.InfoNotification: Notification { - protected override Notify.Notification create_notification () { - return new Notify.Notification (_("Volume"), "", "audio-volume-muted"); - } + protected override Notify.Notification create_notification () { + return new Notify.Notification (_("Volume"), "", "audio-volume-muted"); + } - public void show (VolumeControl.ActiveOutput active_output, - double volume, - bool is_high_volume) { - if (!notify_server_supports ("x-canonical-private-synchronous")) - return; + public void show (VolumeControl.ActiveOutput active_output, + double volume, + bool is_high_volume) { + if (!notify_server_supports ("x-canonical-private-synchronous")) + return; - /* Determine Label */ - unowned string volume_label = get_notification_label (active_output); + /* Determine Label */ + unowned string volume_label = get_notification_label (active_output); - /* Choose an icon */ - unowned string icon = get_volume_notification_icon (active_output, volume, is_high_volume); + /* Choose an icon */ + unowned string icon = get_volume_notification_icon (active_output, volume, is_high_volume); - /* Reset the notification */ - var n = _notification; - n.update (_("Volume"), volume_label, icon); - n.clear_hints(); - n.set_hint ("x-canonical-non-shaped-icon", "true"); - n.set_hint ("x-canonical-private-synchronous", "true"); - n.set_hint ("x-canonical-value-bar-tint", is_high_volume ? "true" : "false"); - n.set_hint ("value", ((int32)((volume * 100.0) + 0.5)).clamp(0, 100)); - show_notification (); - } + /* Reset the notification */ + var n = _notification; + n.update (_("Volume"), volume_label, icon); + n.clear_hints(); + n.set_hint ("x-canonical-non-shaped-icon", "true"); + n.set_hint ("x-canonical-private-synchronous", "true"); + n.set_hint ("x-canonical-value-bar-tint", is_high_volume ? "true" : "false"); + n.set_hint ("value", ((int32)((volume * 100.0) + 0.5)).clamp(0, 100)); + show_notification (); + } - private static unowned string get_notification_label (VolumeControl.ActiveOutput active_output) { + private static unowned string get_notification_label (VolumeControl.ActiveOutput active_output) { - switch (active_output) { - case VolumeControl.ActiveOutput.SPEAKERS: - return _("Speakers"); - case VolumeControl.ActiveOutput.HEADPHONES: - return _("Headphones"); - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - return _("Bluetooth headphones"); - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - return _("Bluetooth speaker"); - case VolumeControl.ActiveOutput.USB_SPEAKER: - return _("Usb speaker"); - case VolumeControl.ActiveOutput.USB_HEADPHONES: - return _("Usb headphones"); - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - return _("HDMI speaker"); - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - return _("HDMI headphones"); - default: - return ""; - } - } + switch (active_output) { + case VolumeControl.ActiveOutput.SPEAKERS: + return _("Speakers"); + case VolumeControl.ActiveOutput.HEADPHONES: + return _("Headphones"); + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + return _("Bluetooth headphones"); + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + return _("Bluetooth speaker"); + case VolumeControl.ActiveOutput.USB_SPEAKER: + return _("Usb speaker"); + case VolumeControl.ActiveOutput.USB_HEADPHONES: + return _("Usb headphones"); + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + return _("HDMI speaker"); + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + return _("HDMI headphones"); + default: + return ""; + } + } - private static unowned string get_volume_notification_icon (VolumeControl.ActiveOutput active_output, - double volume, - bool is_high_volume) { + private static unowned string get_volume_notification_icon (VolumeControl.ActiveOutput active_output, + double volume, + bool is_high_volume) { - if (!is_high_volume) - return get_volume_icon (active_output, volume); + if (!is_high_volume) + return get_volume_icon (active_output, volume); - switch (active_output) { - case VolumeControl.ActiveOutput.SPEAKERS: - case VolumeControl.ActiveOutput.HEADPHONES: - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - case VolumeControl.ActiveOutput.USB_SPEAKER: - case VolumeControl.ActiveOutput.USB_HEADPHONES: - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - return "audio-volume-high"; + switch (active_output) { + case VolumeControl.ActiveOutput.SPEAKERS: + case VolumeControl.ActiveOutput.HEADPHONES: + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + case VolumeControl.ActiveOutput.USB_SPEAKER: + case VolumeControl.ActiveOutput.USB_HEADPHONES: + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + return "audio-volume-high"; - default: - return ""; - } - } + default: + return ""; + } + } - private static unowned string get_volume_icon (VolumeControl.ActiveOutput active_output, - double volume) - { - switch (active_output) { - case VolumeControl.ActiveOutput.SPEAKERS: - case VolumeControl.ActiveOutput.HEADPHONES: - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - case VolumeControl.ActiveOutput.USB_SPEAKER: - case VolumeControl.ActiveOutput.USB_HEADPHONES: - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - if (volume <= 0.0) - return "audio-volume-muted"; - if (volume <= 0.3) - return "audio-volume-low"; - if (volume <= 0.7) - return "audio-volume-medium"; - return "audio-volume-high"; + private static unowned string get_volume_icon (VolumeControl.ActiveOutput active_output, + double volume) + { + switch (active_output) { + case VolumeControl.ActiveOutput.SPEAKERS: + case VolumeControl.ActiveOutput.HEADPHONES: + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + case VolumeControl.ActiveOutput.USB_SPEAKER: + case VolumeControl.ActiveOutput.USB_HEADPHONES: + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + if (volume <= 0.0) + return "audio-volume-muted"; + if (volume <= 0.3) + return "audio-volume-low"; + if (volume <= 0.7) + return "audio-volume-medium"; + return "audio-volume-high"; - default: - return ""; - } - } + default: + return ""; + } + } } diff --git a/src/sound-menu.vala b/src/sound-menu.vala index e0a05a1..957238c 100644 --- a/src/sound-menu.vala +++ b/src/sound-menu.vala @@ -19,456 +19,456 @@ public class SoundMenu: Object { - public enum DisplayFlags { - NONE = 0, - SHOW_MUTE = 1, - HIDE_INACTIVE_PLAYERS = 2, - HIDE_PLAYERS = 4, - GREETER_PLAYERS = 8, - SHOW_SILENT_MODE = 16, - HIDE_INACTIVE_PLAYERS_PLAY_CONTROLS = 32, - ADD_PLAY_CONTROL_INACTIVE_PLAYER = 64 - } - - public enum PlayerSectionPosition { - LABEL = 0, - PLAYER_CONTROLS = 1, - PLAYLIST = 2 - } - - const string PLAYBACK_ITEM_TYPE = "org.ayatana.unity.playback-item"; - - public SoundMenu (string? settings_action, DisplayFlags flags) { - /* A sound menu always has at least two sections: the volume section (this.volume_section) - * at the start of the menu, and the settings section at the end. Between those two, - * it has a dynamic amount of player sections, one for each registered player. - */ - - this.volume_section = new Menu (); - - if ((flags & DisplayFlags.SHOW_MUTE) != 0) - volume_section.append (_("Mute"), "indicator.mute"); - if ((flags & DisplayFlags.SHOW_SILENT_MODE) != 0) { - var item = new MenuItem(_("Silent Mode"), "indicator.silent-mode"); - item.set_attribute("x-canonical-type", "s", "org.ayatana.indicator.switch"); - volume_section.append_item(item); - } - - volume_section.append_item (this.create_slider_menu_item (_("Volume"), "indicator.volume(0)", 0.0, 1.0, 0.01, - "audio-volume-low-zero-panel", - "audio-volume-high-panel", true)); - - this.menu = new Menu (); - this.menu.append_section (null, volume_section); - - if (settings_action != null) { - settings_shown = true; - this.menu.append (_("Sound Settingsā€¦"), settings_action); - } - - var root_item = new MenuItem (null, "indicator.root"); - root_item.set_attribute ("x-canonical-type", "s", "org.ayatana.indicator.root"); - root_item.set_attribute ("x-canonical-scroll-action", "s", "indicator.scroll"); - root_item.set_attribute ("x-canonical-secondary-action", "s", "indicator.mute"); - root_item.set_attribute ("submenu-action", "s", "indicator.indicator-shown"); - root_item.set_submenu (this.menu); - - this.root = new Menu (); - root.append_item (root_item); - - this.hide_players = (flags & DisplayFlags.HIDE_PLAYERS) != 0; - this.hide_inactive = (flags & DisplayFlags.HIDE_INACTIVE_PLAYERS) != 0; - this.hide_inactive_player_controls = (flags & DisplayFlags.HIDE_INACTIVE_PLAYERS_PLAY_CONTROLS) != 0; - this.add_play_button_inactive_player = (flags & DisplayFlags.ADD_PLAY_CONTROL_INACTIVE_PLAYER) != 0; - this.notify_handlers = new HashTable (direct_hash, direct_equal); - - this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0; - } - - ~SoundMenu () { - if (export_id != 0) { - bus.unexport_menu_model(export_id); - export_id = 0; - } - } - - public void set_default_player (string default_player_id) { - this.default_player = default_player_id; - foreach (var player_stored in notify_handlers.get_keys ()) { - int index = this.find_player_section(player_stored); - if (index != -1 && player_stored.id == this.default_player) { - add_player_playback_controls (player_stored, index, true); - } - } - } - - DBusConnection? bus = null; - uint export_id = 0; - - public void export (DBusConnection connection, string object_path) { - bus = connection; - try { - export_id = bus.export_menu_model (object_path, this.root); - } catch (Error e) { - critical ("%s", e.message); - } - } - - public bool show_mic_volume { - get { - return this.mic_volume_shown; - } - set { - if (value && !this.mic_volume_shown) { - var slider = this.create_slider_menu_item (_("Microphone Volume"), "indicator.mic-volume", 0.0, 1.0, 0.01, - "audio-input-microphone-low-zero-panel", - "audio-input-microphone-high-panel", false); - volume_section.append_item (slider); - this.mic_volume_shown = true; - } - else if (!value && this.mic_volume_shown) { - int location = -1; - while ((location = find_action(this.volume_section, "indicator.mic-volume")) != -1) { - this.volume_section.remove (location); - } - this.mic_volume_shown = false; - } - } - } - - public bool show_high_volume_warning { - get { - return this.high_volume_warning_shown; - } - set { - if (value && !this.high_volume_warning_shown) { - /* NOTE: Action doesn't really exist, just used to find below when removing */ - var item = new MenuItem(_("High volume can damage your hearing."), "indicator.high-volume-warning-item"); - volume_section.append_item (item); - this.high_volume_warning_shown = true; - } - else if (!value && this.high_volume_warning_shown) { - int location = -1; - while ((location = find_action(this.volume_section, "indicator.high-volume-warning-item")) != -1) { - this.volume_section.remove (location); - } - this.high_volume_warning_shown = false; - } - } - } - - int find_action (Menu menu, string in_action) { - int n = menu.get_n_items (); - for (int i = 0; i < n; i++) { - string action; - menu.get_item_attribute (i, "action", "s", out action); - if (in_action == action) - return i; - } - - return -1; - } - - public void update_all_players_play_section() { - foreach (var player_stored in notify_handlers.get_keys ()) { - int index = this.find_player_section(player_stored); - if (index != -1) { - // just update to verify if we must hide the player controls - update_player_section (player_stored, index); - } - } - } - - private void check_last_running_player () { - foreach (var player in notify_handlers.get_keys ()) { - if (player.is_running && number_of_running_players == 1) { - // this is the first or the last player running... - // store its id - this.last_player_updated (player.id); - } - } - } - - public void add_player (MediaPlayer player) { - if (this.notify_handlers.contains (player)) - return; - - if (player.is_running || !this.hide_inactive) - this.insert_player_section (player); - this.update_playlists (player); - - var handler_id = player.notify["is-running"].connect ( () => { - int index = this.find_player_section(player); - if (player.is_running) { - if (index == -1) { - this.insert_player_section (player); - } - number_of_running_players++; - } - else { - number_of_running_players--; - if (this.hide_inactive) - this.remove_player_section (player); - } - this.update_playlists (player); - - // we need to update the rest of players, because we might have - // a non running player still showing the playback controls - update_all_players_play_section(); - - check_last_running_player (); - }); - this.notify_handlers.insert (player, handler_id); - - player.playlists_changed.connect (this.update_playlists); - player.playbackstatus_changed.connect (this.update_playbackstatus); - - check_last_running_player (); - } - - public void remove_player (MediaPlayer player) { - this.remove_player_section (player); - - var id = this.notify_handlers.lookup(player); - if (id != 0) { - player.disconnect(id); - } - - player.playlists_changed.disconnect (this.update_playlists); - - /* this'll drop our ref to it */ - this.notify_handlers.remove (player); - - check_last_running_player (); - } - - public void update_volume_slider (VolumeControl.ActiveOutput active_output) { - int index = find_action (this.volume_section, "indicator.volume"); - if (index != -1) { - string label = "Volume"; - switch (active_output) { - case VolumeControl.ActiveOutput.SPEAKERS: - label = _("Volume"); - break; - case VolumeControl.ActiveOutput.HEADPHONES: - label = _("Volume (Headphones)"); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - label = _("Volume (Bluetooth)"); - break; - case VolumeControl.ActiveOutput.USB_SPEAKER: - label = _("Volume (Usb)"); - break; - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - label = _("Volume (HDMI)"); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - label = _("Volume (Bluetooth headphones)"); - break; - case VolumeControl.ActiveOutput.USB_HEADPHONES: - label = _("Volume (Usb headphones)"); - break; - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - label = _("Volume (HDMI headphones)"); - break; - } - this.volume_section.remove (index); - this.volume_section.insert_item (index, this.create_slider_menu_item (_(label), "indicator.volume(0)", 0.0, 1.0, 0.01, - "audio-volume-low-zero-panel", - "audio-volume-high-panel", true)); - } - } - - public Menu root; - public Menu menu; - Menu volume_section; - bool mic_volume_shown; - bool settings_shown = false; - bool high_volume_warning_shown = false; - bool hide_inactive; - bool hide_players = false; - bool hide_inactive_player_controls = false; - bool add_play_button_inactive_player = false; - HashTable notify_handlers; - bool greeter_players = false; - int number_of_running_players = 0; - string default_player = ""; - - /* returns the position in this.menu of the section that's associated with @player */ - int find_player_section (MediaPlayer player) { - debug("Looking for player: %s", player.id); - string action_name = @"indicator.$(player.id)"; - int n = this.menu.get_n_items (); - for (int i = 0; i < n; i++) { - var section = this.menu.get_item_link (i, Menu.LINK_SECTION); - if (section == null) continue; - - string action; - section.get_item_attribute (0, "action", "s", out action); - if (action == action_name) - return i; - } - - debug("Unable to find section for player: %s", player.id); - return -1; - } - - int find_player_playback_controls_section (Menu player_menu) { - int n = player_menu.get_n_items (); - for (int i = 0; i < n; i++) { - string type; - player_menu.get_item_attribute (i, "x-canonical-type", "s", out type); - if (type == PLAYBACK_ITEM_TYPE) - return i; - } - - return -1; - } - - MenuItem create_playback_menu_item (MediaPlayer player) { - var playback_item = new MenuItem (null, null); - playback_item.set_attribute ("x-canonical-type", "s", PLAYBACK_ITEM_TYPE); - if (player.is_running) { - if (player.can_do_play) { - playback_item.set_attribute ("x-canonical-play-action", "s", "indicator.play." + player.id); - } - if (player.can_do_next) { - playback_item.set_attribute ("x-canonical-next-action", "s", "indicator.next." + player.id); - } - if (player.can_do_prev) { - playback_item.set_attribute ("x-canonical-previous-action", "s", "indicator.previous." + player.id); - } - } else { - if (this.add_play_button_inactive_player) { - playback_item.set_attribute ("x-canonical-play-action", "s", "indicator.play." + player.id); - } - } - return playback_item; - } - - void insert_player_section (MediaPlayer player) { - if (this.hide_players) - return; - - var section = new Menu (); - Icon icon; - - debug("Adding section for player: %s (%s)", player.id, player.is_running ? "running" : "not running"); - - icon = player.icon; - if (icon == null) - icon = new ThemedIcon.with_default_fallbacks ("application-default-icon"); - - var base_action = "indicator." + player.id; - if (this.greeter_players) - base_action += ".greeter"; - - var player_item = new MenuItem (player.name, base_action); - player_item.set_attribute ("x-canonical-type", "s", "org.ayatana.unity.media-player"); - if (icon != null) - player_item.set_attribute_value ("icon", icon.serialize ()); - section.append_item (player_item); - - if (player.is_running|| !this.hide_inactive_player_controls || player.id == this.default_player) { - var playback_item = create_playback_menu_item (player); - section.insert_item (PlayerSectionPosition.PLAYER_CONTROLS, playback_item); - } - - /* Add new players to the end of the player sections, just before the settings */ - if (settings_shown) { - this.menu.insert_section (this.menu.get_n_items () -1, null, section); - } else { - this.menu.append_section (null, section); - } - } - - void remove_player_section (MediaPlayer player) { - if (this.hide_players) - return; - - int index = this.find_player_section (player); - if (index >= 0) - this.menu.remove (index); - } - - void add_player_playback_controls (MediaPlayer player, int index, bool adding_default_player) { - var player_section = this.menu.get_item_link(index, Menu.LINK_SECTION) as Menu; - - int play_control_index = find_player_playback_controls_section (player_section); - if (player.is_running || !this.hide_inactive_player_controls || (number_of_running_players == 0 && adding_default_player) ) { - MenuItem playback_item = create_playback_menu_item (player); - if (play_control_index != -1) { - player_section.remove (PlayerSectionPosition.PLAYER_CONTROLS); - } - player_section.insert_item (PlayerSectionPosition.PLAYER_CONTROLS, playback_item); - } else { - if (play_control_index != -1 && number_of_running_players >= 1) { - // remove both, playlist and play controls - player_section.remove (PlayerSectionPosition.PLAYLIST); - player_section.remove (PlayerSectionPosition.PLAYER_CONTROLS); - } - } - } - - void update_player_section (MediaPlayer player, int index) { - add_player_playback_controls (player, index, false); - } - - void update_playlists (MediaPlayer player) { - int index = find_player_section (player); - if (index < 0) - return; - - var player_section = this.menu.get_item_link (index, Menu.LINK_SECTION) as Menu; - - /* if a section has three items, the playlists menu is in it */ - if (player_section.get_n_items () == 3) - player_section.remove (2); - - if (!player.is_running) - return; - - var count = player.get_n_playlists (); - if (count == 0) - return; - - var playlists_section = new Menu (); - for (int i = 0; i < count; i++) { - var playlist_id = player.get_playlist_id (i); - playlists_section.append (player.get_playlist_name (i), - @"indicator.play-playlist.$(player.id)::$playlist_id"); - - } - - var submenu = new Menu (); - submenu.append_section (null, playlists_section); - player_section.append_submenu (_("Choose Playlist"), submenu); - } - - void update_playbackstatus (MediaPlayer player) { - int index = find_player_section (player); - if (index != -1) { - update_player_section (player, index); - } - } - - MenuItem create_slider_menu_item (string label, string action, double min, double max, double step, string min_icon_name, string max_icon_name, bool sync_action) { - var min_icon = new ThemedIcon.with_default_fallbacks (min_icon_name); - var max_icon = new ThemedIcon.with_default_fallbacks (max_icon_name); - - var slider = new MenuItem (label, action); - slider.set_attribute ("x-canonical-type", "s", "org.ayatana.unity.slider"); - slider.set_attribute_value ("min-icon", min_icon.serialize ()); - slider.set_attribute_value ("max-icon", max_icon.serialize ()); - slider.set_attribute ("min-value", "d", min); - slider.set_attribute ("max-value", "d", max); - slider.set_attribute ("step", "d", step); - if (sync_action) { - slider.set_attribute ("x-canonical-sync-action", "s", "indicator.volume-sync"); - } - - return slider; - } - - public signal void last_player_updated (string player_id); + public enum DisplayFlags { + NONE = 0, + SHOW_MUTE = 1, + HIDE_INACTIVE_PLAYERS = 2, + HIDE_PLAYERS = 4, + GREETER_PLAYERS = 8, + SHOW_SILENT_MODE = 16, + HIDE_INACTIVE_PLAYERS_PLAY_CONTROLS = 32, + ADD_PLAY_CONTROL_INACTIVE_PLAYER = 64 + } + + public enum PlayerSectionPosition { + LABEL = 0, + PLAYER_CONTROLS = 1, + PLAYLIST = 2 + } + + const string PLAYBACK_ITEM_TYPE = "org.ayatana.unity.playback-item"; + + public SoundMenu (string? settings_action, DisplayFlags flags) { + /* A sound menu always has at least two sections: the volume section (this.volume_section) + * at the start of the menu, and the settings section at the end. Between those two, + * it has a dynamic amount of player sections, one for each registered player. + */ + + this.volume_section = new Menu (); + + if ((flags & DisplayFlags.SHOW_MUTE) != 0) + volume_section.append (_("Mute"), "indicator.mute"); + if ((flags & DisplayFlags.SHOW_SILENT_MODE) != 0) { + var item = new MenuItem(_("Silent Mode"), "indicator.silent-mode"); + item.set_attribute("x-ayatana-type", "s", "org.ayatana.indicator.switch"); + volume_section.append_item(item); + } + + volume_section.append_item (this.create_slider_menu_item (_("Volume"), "indicator.volume(0)", 0.0, 1.0, 0.01, + "audio-volume-low-zero-panel", + "audio-volume-high-panel", true)); + + this.menu = new Menu (); + this.menu.append_section (null, volume_section); + + if (settings_action != null) { + settings_shown = true; + this.menu.append (_("Sound Settingsā€¦"), settings_action); + } + + var root_item = new MenuItem (null, "indicator.root"); + root_item.set_attribute ("x-ayatana-type", "s", "org.ayatana.indicator.root"); + root_item.set_attribute ("x-ayatana-scroll-action", "s", "indicator.scroll"); + root_item.set_attribute ("x-ayatana-secondary-action", "s", "indicator.mute"); + root_item.set_attribute ("submenu-action", "s", "indicator.indicator-shown"); + root_item.set_submenu (this.menu); + + this.root = new Menu (); + root.append_item (root_item); + + this.hide_players = (flags & DisplayFlags.HIDE_PLAYERS) != 0; + this.hide_inactive = (flags & DisplayFlags.HIDE_INACTIVE_PLAYERS) != 0; + this.hide_inactive_player_controls = (flags & DisplayFlags.HIDE_INACTIVE_PLAYERS_PLAY_CONTROLS) != 0; + this.add_play_button_inactive_player = (flags & DisplayFlags.ADD_PLAY_CONTROL_INACTIVE_PLAYER) != 0; + this.notify_handlers = new HashTable (direct_hash, direct_equal); + + this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0; + } + + ~SoundMenu () { + if (export_id != 0) { + bus.unexport_menu_model(export_id); + export_id = 0; + } + } + + public void set_default_player (string default_player_id) { + this.default_player = default_player_id; + foreach (var player_stored in notify_handlers.get_keys ()) { + int index = this.find_player_section(player_stored); + if (index != -1 && player_stored.id == this.default_player) { + add_player_playback_controls (player_stored, index, true); + } + } + } + + DBusConnection? bus = null; + uint export_id = 0; + + public void export (DBusConnection connection, string object_path) { + bus = connection; + try { + export_id = bus.export_menu_model (object_path, this.root); + } catch (Error e) { + critical ("%s", e.message); + } + } + + public bool show_mic_volume { + get { + return this.mic_volume_shown; + } + set { + if (value && !this.mic_volume_shown) { + var slider = this.create_slider_menu_item (_("Microphone Volume"), "indicator.mic-volume", 0.0, 1.0, 0.01, + "audio-input-microphone-low-zero-panel", + "audio-input-microphone-high-panel", false); + volume_section.append_item (slider); + this.mic_volume_shown = true; + } + else if (!value && this.mic_volume_shown) { + int location = -1; + while ((location = find_action(this.volume_section, "indicator.mic-volume")) != -1) { + this.volume_section.remove (location); + } + this.mic_volume_shown = false; + } + } + } + + public bool show_high_volume_warning { + get { + return this.high_volume_warning_shown; + } + set { + if (value && !this.high_volume_warning_shown) { + /* NOTE: Action doesn't really exist, just used to find below when removing */ + var item = new MenuItem(_("High volume can damage your hearing."), "indicator.high-volume-warning-item"); + volume_section.append_item (item); + this.high_volume_warning_shown = true; + } + else if (!value && this.high_volume_warning_shown) { + int location = -1; + while ((location = find_action(this.volume_section, "indicator.high-volume-warning-item")) != -1) { + this.volume_section.remove (location); + } + this.high_volume_warning_shown = false; + } + } + } + + int find_action (Menu menu, string in_action) { + int n = menu.get_n_items (); + for (int i = 0; i < n; i++) { + string action; + menu.get_item_attribute (i, "action", "s", out action); + if (in_action == action) + return i; + } + + return -1; + } + + public void update_all_players_play_section() { + foreach (var player_stored in notify_handlers.get_keys ()) { + int index = this.find_player_section(player_stored); + if (index != -1) { + // just update to verify if we must hide the player controls + update_player_section (player_stored, index); + } + } + } + + private void check_last_running_player () { + foreach (var player in notify_handlers.get_keys ()) { + if (player.is_running && number_of_running_players == 1) { + // this is the first or the last player running... + // store its id + this.last_player_updated (player.id); + } + } + } + + public void add_player (MediaPlayer player) { + if (this.notify_handlers.contains (player)) + return; + + if (player.is_running || !this.hide_inactive) + this.insert_player_section (player); + this.update_playlists (player); + + var handler_id = player.notify["is-running"].connect ( () => { + int index = this.find_player_section(player); + if (player.is_running) { + if (index == -1) { + this.insert_player_section (player); + } + number_of_running_players++; + } + else { + number_of_running_players--; + if (this.hide_inactive) + this.remove_player_section (player); + } + this.update_playlists (player); + + // we need to update the rest of players, because we might have + // a non running player still showing the playback controls + update_all_players_play_section(); + + check_last_running_player (); + }); + this.notify_handlers.insert (player, handler_id); + + player.playlists_changed.connect (this.update_playlists); + player.playbackstatus_changed.connect (this.update_playbackstatus); + + check_last_running_player (); + } + + public void remove_player (MediaPlayer player) { + this.remove_player_section (player); + + var id = this.notify_handlers.lookup(player); + if (id != 0) { + player.disconnect(id); + } + + player.playlists_changed.disconnect (this.update_playlists); + + /* this'll drop our ref to it */ + this.notify_handlers.remove (player); + + check_last_running_player (); + } + + public void update_volume_slider (VolumeControl.ActiveOutput active_output) { + int index = find_action (this.volume_section, "indicator.volume"); + if (index != -1) { + string label = "Volume"; + switch (active_output) { + case VolumeControl.ActiveOutput.SPEAKERS: + label = _("Volume"); + break; + case VolumeControl.ActiveOutput.HEADPHONES: + label = _("Volume (Headphones)"); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + label = _("Volume (Bluetooth)"); + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + label = _("Volume (Usb)"); + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + label = _("Volume (HDMI)"); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + label = _("Volume (Bluetooth headphones)"); + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + label = _("Volume (Usb headphones)"); + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + label = _("Volume (HDMI headphones)"); + break; + } + this.volume_section.remove (index); + this.volume_section.insert_item (index, this.create_slider_menu_item (_(label), "indicator.volume(0)", 0.0, 1.0, 0.01, + "audio-volume-low-zero-panel", + "audio-volume-high-panel", true)); + } + } + + public Menu root; + public Menu menu; + Menu volume_section; + bool mic_volume_shown; + bool settings_shown = false; + bool high_volume_warning_shown = false; + bool hide_inactive; + bool hide_players = false; + bool hide_inactive_player_controls = false; + bool add_play_button_inactive_player = false; + HashTable notify_handlers; + bool greeter_players = false; + int number_of_running_players = 0; + string default_player = ""; + + /* returns the position in this.menu of the section that's associated with @player */ + int find_player_section (MediaPlayer player) { + debug("Looking for player: %s", player.id); + string action_name = @"indicator.$(player.id)"; + int n = this.menu.get_n_items (); + for (int i = 0; i < n; i++) { + var section = this.menu.get_item_link (i, Menu.LINK_SECTION); + if (section == null) continue; + + string action; + section.get_item_attribute (0, "action", "s", out action); + if (action == action_name) + return i; + } + + debug("Unable to find section for player: %s", player.id); + return -1; + } + + int find_player_playback_controls_section (Menu player_menu) { + int n = player_menu.get_n_items (); + for (int i = 0; i < n; i++) { + string type; + player_menu.get_item_attribute (i, "x-ayatana-type", "s", out type); + if (type == PLAYBACK_ITEM_TYPE) + return i; + } + + return -1; + } + + MenuItem create_playback_menu_item (MediaPlayer player) { + var playback_item = new MenuItem (null, null); + playback_item.set_attribute ("x-ayatana-type", "s", PLAYBACK_ITEM_TYPE); + if (player.is_running) { + if (player.can_do_play) { + playback_item.set_attribute ("x-ayatana-play-action", "s", "indicator.play." + player.id); + } + if (player.can_do_next) { + playback_item.set_attribute ("x-ayatana-next-action", "s", "indicator.next." + player.id); + } + if (player.can_do_prev) { + playback_item.set_attribute ("x-ayatana-previous-action", "s", "indicator.previous." + player.id); + } + } else { + if (this.add_play_button_inactive_player) { + playback_item.set_attribute ("x-ayatana-play-action", "s", "indicator.play." + player.id); + } + } + return playback_item; + } + + void insert_player_section (MediaPlayer player) { + if (this.hide_players) + return; + + var section = new Menu (); + Icon icon; + + debug("Adding section for player: %s (%s)", player.id, player.is_running ? "running" : "not running"); + + icon = player.icon; + if (icon == null) + icon = new ThemedIcon.with_default_fallbacks ("application-default-icon"); + + var base_action = "indicator." + player.id; + if (this.greeter_players) + base_action += ".greeter"; + + var player_item = new MenuItem (player.name, base_action); + player_item.set_attribute ("x-ayatana-type", "s", "org.ayatana.unity.media-player"); + if (icon != null) + player_item.set_attribute_value ("icon", icon.serialize ()); + section.append_item (player_item); + + if (player.is_running|| !this.hide_inactive_player_controls || player.id == this.default_player) { + var playback_item = create_playback_menu_item (player); + section.insert_item (PlayerSectionPosition.PLAYER_CONTROLS, playback_item); + } + + /* Add new players to the end of the player sections, just before the settings */ + if (settings_shown) { + this.menu.insert_section (this.menu.get_n_items () -1, null, section); + } else { + this.menu.append_section (null, section); + } + } + + void remove_player_section (MediaPlayer player) { + if (this.hide_players) + return; + + int index = this.find_player_section (player); + if (index >= 0) + this.menu.remove (index); + } + + void add_player_playback_controls (MediaPlayer player, int index, bool adding_default_player) { + var player_section = this.menu.get_item_link(index, Menu.LINK_SECTION) as Menu; + + int play_control_index = find_player_playback_controls_section (player_section); + if (player.is_running || !this.hide_inactive_player_controls || (number_of_running_players == 0 && adding_default_player) ) { + MenuItem playback_item = create_playback_menu_item (player); + if (play_control_index != -1) { + player_section.remove (PlayerSectionPosition.PLAYER_CONTROLS); + } + player_section.insert_item (PlayerSectionPosition.PLAYER_CONTROLS, playback_item); + } else { + if (play_control_index != -1 && number_of_running_players >= 1) { + // remove both, playlist and play controls + player_section.remove (PlayerSectionPosition.PLAYLIST); + player_section.remove (PlayerSectionPosition.PLAYER_CONTROLS); + } + } + } + + void update_player_section (MediaPlayer player, int index) { + add_player_playback_controls (player, index, false); + } + + void update_playlists (MediaPlayer player) { + int index = find_player_section (player); + if (index < 0) + return; + + var player_section = this.menu.get_item_link (index, Menu.LINK_SECTION) as Menu; + + /* if a section has three items, the playlists menu is in it */ + if (player_section.get_n_items () == 3) + player_section.remove (2); + + if (!player.is_running) + return; + + var count = player.get_n_playlists (); + if (count == 0) + return; + + var playlists_section = new Menu (); + for (int i = 0; i < count; i++) { + var playlist_id = player.get_playlist_id (i); + playlists_section.append (player.get_playlist_name (i), + @"indicator.play-playlist.$(player.id)::$playlist_id"); + + } + + var submenu = new Menu (); + submenu.append_section (null, playlists_section); + player_section.append_submenu (_("Choose Playlist"), submenu); + } + + void update_playbackstatus (MediaPlayer player) { + int index = find_player_section (player); + if (index != -1) { + update_player_section (player, index); + } + } + + MenuItem create_slider_menu_item (string label, string action, double min, double max, double step, string min_icon_name, string max_icon_name, bool sync_action) { + var min_icon = new ThemedIcon.with_default_fallbacks (min_icon_name); + var max_icon = new ThemedIcon.with_default_fallbacks (max_icon_name); + + var slider = new MenuItem (label, action); + slider.set_attribute ("x-ayatana-type", "s", "org.ayatana.unity.slider"); + slider.set_attribute_value ("min-icon", min_icon.serialize ()); + slider.set_attribute_value ("max-icon", max_icon.serialize ()); + slider.set_attribute ("min-value", "d", min); + slider.set_attribute ("max-value", "d", max); + slider.set_attribute ("step", "d", step); + if (sync_action) { + slider.set_attribute ("x-ayatana-sync-action", "s", "indicator.volume-sync"); + } + + return slider; + } + + public signal void last_player_updated (string player_id); } diff --git a/src/warn-notification.vala b/src/warn-notification.vala index 17129aa..f5c8b0b 100644 --- a/src/warn-notification.vala +++ b/src/warn-notification.vala @@ -19,41 +19,41 @@ public class IndicatorSound.WarnNotification: Notification { - public enum Response { - CANCEL, - OK - } + public enum Response { + CANCEL, + OK + } - public signal void user_responded (Response response); + public signal void user_responded (Response response); protected override Notify.Notification create_notification () { - var n = new Notify.Notification ( - _("Volume"), - _("High volume can damage your hearing."), - "audio-volume-high"); - n.set_hint ("x-canonical-non-shaped-icon", "true"); - n.set_hint ("x-canonical-snap-decisions", "true"); - n.set_hint ("x-canonical-private-affirmative-tint", "true"); - n.closed.connect ((n) => { - n.clear_actions (); - }); - return n; - } + var n = new Notify.Notification ( + _("Volume"), + _("High volume can damage your hearing."), + "audio-volume-high"); + n.set_hint ("x-canonical-non-shaped-icon", "true"); + n.set_hint ("x-canonical-snap-decisions", "true"); + n.set_hint ("x-canonical-private-affirmative-tint", "true"); + n.closed.connect ((n) => { + n.clear_actions (); + }); + return n; + } - public bool show () { + public bool show () { - if (!notify_server_supports ("actions")) - return false; + if (!notify_server_supports ("actions")) + return false; - _notification.clear_actions (); - _notification.add_action ("ok", _("OK"), (n, a) => { - user_responded (Response.OK); - }); - _notification.add_action ("cancel", _("Cancel"), (n, a) => { - user_responded (Response.CANCEL); - }); - show_notification(); + _notification.clear_actions (); + _notification.add_action ("ok", _("OK"), (n, a) => { + user_responded (Response.OK); + }); + _notification.add_action ("cancel", _("Cancel"), (n, a) => { + user_responded (Response.CANCEL); + }); + show_notification(); - return true; - } + return true; + } } -- cgit v1.2.3