/* * Copyright 2013 Canonical Ltd. * * 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; version 3. * * 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 Lesser General Public License * along with this program. If not, see . * * Authors: * Charles Kerr */ class Desktop: Profile { private uint idle_rebuild_id = 0; private Settings settings; private SimpleActionGroup action_group; private Menu device_section; private HashTable connect_actions; protected override void dispose () { if (idle_rebuild_id != 0) { Source.remove (idle_rebuild_id); idle_rebuild_id = 0; } base.dispose (); } public Desktop (Bluetooth bluetooth, SimpleActionGroup action_group) { const string profile_name = "desktop"; base (bluetooth, profile_name); this.action_group = action_group; connect_actions = new HashTable(direct_hash, direct_equal); settings = new Settings ("com.canonical.indicator.bluetooth"); // build the static actions Action[] actions = {}; actions += get_root_action (profile_name); actions += create_enabled_action (bluetooth); actions += create_discoverable_action (bluetooth); actions += create_wizard_action (); actions += create_browse_files_action (); actions += create_send_file_action (); actions += create_show_settings_action (); foreach (var a in actions) action_group.insert (a); build_menu (); // know when to show the indicator & when to hide it settings.changed["visible"].connect (()=> update_visibility()); bluetooth.notify.connect (() => update_visibility()); update_visibility (); // when devices change, rebuild our device section bluetooth.devices_changed.connect (()=> { if (idle_rebuild_id == 0) idle_rebuild_id = Idle.add (() => { rebuild_device_section (); idle_rebuild_id = 0; return false; }); }); } void update_visibility () { visible = bluetooth.powered && !bluetooth.blocked && settings.get_boolean("visible"); } /// /// MenuItems /// MenuItem create_device_connection_menuitem (Device device) { var id = device.id; var action_name = @"desktop-device-$(id)-connected"; var item = new MenuItem (_("Connection"), @"indicator.$action_name"); item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); // if this doesn't already have an action, create one if (!connect_actions.contains (id)) { debug (@"creating action for $action_name"); var a = new SimpleAction.stateful (action_name, null, device.is_connected); a.activate.connect (() => a.set_state (!a.get_state().get_boolean())); a.notify["state"].connect (() => bluetooth.set_device_connected (id, a.get_state().get_boolean())); connect_actions.insert (device.id, a); action_group.insert (a); } else { debug (@"updating action $(device.id) state to $(device.is_connected)"); var action = connect_actions.lookup (device.id); action.set_state (device.is_connected); } return item; } void rebuild_device_section () { device_section.remove_all (); foreach (var device in bluetooth.get_devices()) { Menu submenu = new Menu (); MenuItem item; if (device.is_connectable) submenu.append_item (create_device_connection_menuitem (device)); if (device.supports_browsing) submenu.append (_("Browse files…"), @"indicator.desktop-browse-files::$(device.address)"); if (device.supports_file_transfer) submenu.append (_("Send files…"), @"indicator.desktop-send-file::$(device.address)"); switch (device.device_type) { case Device.Type.KEYBOARD: submenu.append (_("Keyboard Settings…"), "indicator.desktop-show-settings::keyboard"); break; case Device.Type.MOUSE: case Device.Type.TABLET: submenu.append (_("Mouse and Touchpad Settings…"), "indicator.desktop-show-settings::mouse"); break; case Device.Type.HEADSET: case Device.Type.HEADPHONES: case Device.Type.OTHER_AUDIO: submenu.append (_("Sound Settings…"), "indicator.desktop-show-settings::sound"); break; } // only show the device if it's got actions that we can perform on it if (submenu.get_n_items () > 0) { item = new MenuItem (device.name, null); item.set_attribute_value ("icon", device.icon.serialize()); item.set_submenu (submenu); device_section.append_item (item); } } } void build_menu () { Menu section; MenuItem item; // quick toggles section section = new Menu (); section.append_item (create_enabled_menuitem ()); item = new MenuItem ("Visible", "indicator.desktop-discoverable"); item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); section.append_item (item); menu.append_section (null, section); // devices section device_section = new Menu (); rebuild_device_section (); menu.append_section (null, device_section); // settings section section = new Menu (); section.append (_("Set Up New Device…"), "indicator.desktop-wizard"); section.append (_("Bluetooth Settings…"), "indicator.desktop-show-settings::bluetooth"); menu.append_section (null, section); } /// /// Actions /// void show_settings (string panel) { spawn_command_line_async ("gnome-control-center " + panel); } Action create_discoverable_action (Bluetooth bluetooth) { var action = new SimpleAction.stateful ("desktop-discoverable", null, bluetooth.discoverable); action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); action.notify["state"].connect (() => bluetooth.try_set_discoverable (action.get_state().get_boolean())); bluetooth.notify["discoverable"].connect (() => action.set_state (bluetooth.discoverable)); action.set_enabled (bluetooth.powered); bluetooth.notify["powered"].connect (() => action.set_enabled (bluetooth.powered)); return action; } Action create_wizard_action () { var action = new SimpleAction ("desktop-wizard", null); action.activate.connect (() => spawn_command_line_async ("bluetooth-wizard")); return action; } Action create_browse_files_action () { var action = new SimpleAction ("desktop-browse-files", VariantType.STRING); action.activate.connect ((action, address) => { var uri = @"obex://[$(address.get_string())]/"; var file = File.new_for_uri (uri); file.mount_enclosing_volume.begin (MountMountFlags.NONE, null, null, (obj, res) => { try { AppInfo.launch_default_for_uri (uri, null); } catch (Error e) { warning (@"unable to launch '$uri': $(e.message)"); } }); }); return action; } Action create_send_file_action () { var action = new SimpleAction ("desktop-send-file", VariantType.STRING); action.activate.connect ((action, address) => { var cmd = @"bluetooth-sendto --device=$(address.get_string())"; spawn_command_line_async (cmd); }); return action; } Action create_show_settings_action () { var action = new SimpleAction ("desktop-show-settings", VariantType.STRING); action.activate.connect ((action, panel) => show_settings (panel.get_string())); return action; } }