Skip to content

Instantly share code, notes, and snippets.

@travisbhartwell
Created April 14, 2010 21:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save travisbhartwell/366360 to your computer and use it in GitHub Desktop.
Save travisbhartwell/366360 to your computer and use it in GitHub Desktop.
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2006-2007 Bastien Nocera <hadess@hadess.net>
*
*
* 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
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <glib/gprintf.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>
#include <unique/uniqueapp.h>
#ifdef HAVE_APP_INDICATOR
#include <libappindicator/app-indicator.h>
#endif /* HAVE_APP_INDICATOR */
#include <bluetooth-client.h>
#include <bluetooth-client-private.h>
#include <bluetooth-chooser.h>
#include <bluetooth-killswitch.h>
#include "notify.h"
#include "agent.h"
static gboolean option_debug = FALSE;
static BluetoothClient *client;
static GtkTreeModel *devices_model;
static guint num_adapters_present = 0;
static guint num_adapters_powered = 0;
static gboolean show_icon_pref = TRUE;
static gboolean discover_lock = FALSE;
#define PREF_DIR "/apps/bluetooth-manager"
#define PREF_SHOW_ICON PREF_DIR "/show_icon"
#define KEYBOARD_PREFS "gnome-keyboard-properties"
#define MOUSE_PREFS "gnome-mouse-properties"
#define SOUND_PREFS "gnome-volume-control"
#define SOUND_PREFS_CMDLINE SOUND_PREFS " -p hardware"
enum {
CONNECTED,
DISCONNECTED,
CONNECTING,
DISCONNECTING
};
static GConfClient* gconf;
static BluetoothKillswitch *killswitch = NULL;
static GtkBuilder *xml = NULL;
static GtkActionGroup *devices_action_group = NULL;
/* Signal callbacks */
void settings_callback(GObject *widget, gpointer user_data);
void quit_callback(GObject *widget, gpointer user_data);
void browse_callback(GObject *widget, gpointer user_data);
void bluetooth_status_callback (GObject *widget, gpointer user_data);
void bluetooth_discoverable_callback (GtkToggleAction *toggleaction, gpointer user_data);
void wizard_callback(GObject *widget, gpointer user_data);
void sendto_callback(GObject *widget, gpointer user_data);
static void action_set_bold (GtkUIManager *manager, GtkAction *action, const char *path);
void quit_callback(GObject *widget, gpointer user_data)
{
gtk_main_quit ();
}
void settings_callback(GObject *widget, gpointer user_data)
{
const char *command = "bluetooth-properties";
if (!g_spawn_command_line_async(command, NULL))
g_printerr("Couldn't execute command: %s\n", command);
}
static void
select_device_changed(BluetoothChooser *sel,
char *address,
gpointer user_data)
{
GtkDialog *dialog = user_data;
gtk_dialog_set_response_sensitive(dialog,
GTK_RESPONSE_ACCEPT, address != NULL);
}
static void
mount_finish_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GError *error = NULL;
char *uri;
if (g_file_mount_enclosing_volume_finish (G_FILE (source_object),
res, &error) == FALSE) {
g_printerr ("Failed to mount OBEX volume: %s", error->message);
g_error_free (error);
return;
}
uri = g_file_get_uri (G_FILE (source_object));
if (gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error) == FALSE) {
g_printerr ("Failed to open %s: %s", uri, error->message);
g_error_free (error);
}
g_free (uri);
}
void browse_callback(GObject *widget, gpointer user_data)
{
GFile *file;
char *address, *uri;
address = g_strdup (g_object_get_data (widget, "address"));
if (address == NULL) {
GtkWidget *dialog, *selector, *content_area;
int response_id;
dialog = gtk_dialog_new_with_buttons(_("Select Device to Browse"), NULL,
GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
NULL);
gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Browse"), GTK_RESPONSE_ACCEPT);
gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
GTK_RESPONSE_ACCEPT, FALSE);
gtk_window_set_default_size(GTK_WINDOW(dialog), 480, 400);
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_box_set_spacing (GTK_BOX (content_area), 2);
selector = bluetooth_chooser_new(_("Select device to browse"));
gtk_container_set_border_width(GTK_CONTAINER(selector), 5);
gtk_widget_show(selector);
g_object_set(selector,
"show-searching", FALSE,
"show-device-category", FALSE,
"show-device-type", TRUE,
"device-category-filter", BLUETOOTH_CATEGORY_PAIRED_OR_TRUSTED,
"device-service-filter", "OBEXFileTransfer",
NULL);
g_signal_connect(selector, "selected-device-changed",
G_CALLBACK(select_device_changed), dialog);
gtk_container_add (GTK_CONTAINER (content_area), selector);
address = NULL;
response_id = gtk_dialog_run (GTK_DIALOG (dialog));
if (response_id == GTK_RESPONSE_ACCEPT)
g_object_get (G_OBJECT (selector), "device-selected", &address, NULL);
gtk_widget_destroy (dialog);
if (response_id != GTK_RESPONSE_ACCEPT)
return;
}
uri = g_strdup_printf ("obex://[%s]/", address);
g_free (address);
file = g_file_new_for_uri (uri);
g_free (uri);
g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE, NULL, NULL, mount_finish_cb, NULL);
g_object_unref (file);
}
void sendto_callback(GObject *widget, gpointer user_data)
{
GPtrArray *a;
GError *err = NULL;
guint i;
const char *address, *alias;
address = g_object_get_data (widget, "address");
alias = g_object_get_data (widget, "alias");
a = g_ptr_array_new ();
g_ptr_array_add (a, "bluetooth-sendto");
if (address != NULL) {
char *s;
s = g_strdup_printf ("--device=\"%s\"", address);
g_ptr_array_add (a, s);
}
if (address != NULL && alias != NULL) {
char *s;
s = g_strdup_printf ("--name=\"%s\"", alias);
g_ptr_array_add (a, s);
}
g_ptr_array_add (a, NULL);
if (g_spawn_async(NULL, (char **) a->pdata, NULL,
G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err) == FALSE) {
g_printerr("Couldn't execute command: %s\n", err->message);
g_error_free (err);
}
for (i = 1; a->pdata[i] != NULL; i++)
g_free (a->pdata[i]);
g_ptr_array_free (a, TRUE);
}
static void keyboard_callback(GObject *widget, gpointer user_data)
{
const char *command = KEYBOARD_PREFS;
if (!g_spawn_command_line_async(command, NULL))
g_printerr("Couldn't execute command: %s\n", command);
}
static void mouse_callback(GObject *widget, gpointer user_data)
{
const char *command = MOUSE_PREFS;
if (!g_spawn_command_line_async(command, NULL))
g_printerr("Couldn't execute command: %s\n", command);
}
static void sound_callback(GObject *widget, gpointer user_data)
{
const char *command = SOUND_PREFS_CMDLINE;
if (!g_spawn_command_line_async(command, NULL))
g_printerr("Couldn't execute command: %s\n", command);
}
void wizard_callback(GObject *widget, gpointer user_data)
{
const char *command = "bluetooth-wizard";
if (!g_spawn_command_line_async(command, NULL))
g_printerr("Couldn't execute command: %s\n", command);
}
void bluetooth_status_callback (GObject *widget, gpointer user_data)
{
GObject *object;
gboolean active;
object = gtk_builder_get_object (xml, "killswitch");
active = GPOINTER_TO_INT (g_object_get_data (object, "bt-active"));
active = !active;
bluetooth_killswitch_set_state (killswitch,
active ? KILLSWITCH_STATE_UNBLOCKED : KILLSWITCH_STATE_SOFT_BLOCKED);
g_object_set_data (object, "bt-active", GINT_TO_POINTER (active));
}
void
bluetooth_discoverable_callback (GtkToggleAction *toggleaction, gpointer user_data)
{
gboolean discoverable;
discoverable = gtk_toggle_action_get_active (toggleaction);
discover_lock = TRUE;
bluetooth_client_set_discoverable (client, discoverable);
discover_lock = FALSE;
}
static gboolean program_available(const char *program)
{
gchar *path;
path = g_find_program_in_path(program);
if (path == NULL)
return FALSE;
g_free(path);
return TRUE;
}
static void popup_callback(GObject *widget, guint button,
guint activate_time, gpointer user_data)
{
GtkWidget *menu = user_data;
gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
gtk_status_icon_position_menu,
GTK_STATUS_ICON(widget), button, activate_time);
}
static void activate_callback(GObject *widget, gpointer user_data)
{
guint32 activate_time = gtk_get_current_event_time();
if (query_blinking() == TRUE) {
show_agents();
return;
}
popup_callback(widget, 0, activate_time, user_data);
}
static void
killswitch_state_changed (BluetoothKillswitch *killswitch, KillswitchState state)
{
GObject *object;
gboolean sensitive = TRUE;
gboolean bstate = FALSE;
const char *label, *status_label;
g_printf("killswitch_state_changed\n");
if (state == KILLSWITCH_STATE_NO_ADAPTER) {
object = gtk_builder_get_object (xml, "bluetooth-applet-popup");
gtk_menu_popdown (GTK_MENU (object));
return;
}
if (state == KILLSWITCH_STATE_SOFT_BLOCKED) {
label = N_("Turn On Bluetooth");
status_label = N_("Bluetooth: Off");
bstate = FALSE;
} else if (state == KILLSWITCH_STATE_UNBLOCKED) {
label = N_("Turn Off Bluetooth");
status_label = N_("Bluetooth: On");
bstate = TRUE;
} else if (state == KILLSWITCH_STATE_HARD_BLOCKED) {
sensitive = FALSE;
label = NULL;
status_label = N_("Bluetooth: Disabled");
} else {
g_assert_not_reached ();
}
object = gtk_builder_get_object (xml, "killswitch-label");
gtk_action_set_label (GTK_ACTION (object), _(status_label));
gtk_action_set_visible (GTK_ACTION (object), TRUE);
object = gtk_builder_get_object (xml, "killswitch");
gtk_action_set_visible (GTK_ACTION (object), sensitive);
gtk_action_set_label (GTK_ACTION (object), _(label));
if (sensitive != FALSE) {
gtk_action_set_label (GTK_ACTION (object), _(label));
g_object_set_data (object, "bt-active", GINT_TO_POINTER (bstate));
}
object = gtk_builder_get_object (xml, "bluetooth-applet-ui-manager");
gtk_ui_manager_ensure_update (GTK_UI_MANAGER (object));
}
static GtkWidget *create_popupmenu(void)
{
GObject *object;
GError *error = NULL;
#ifdef HAVE_APP_INDICATOR
GtkWidget *menuitem = NULL;
#endif /* HAVE_APP_INDICATOR */
g_printf("create_popupmenu\n");
xml = gtk_builder_new ();
if (gtk_builder_add_from_file (xml, "popup-menu.ui", &error) == 0) {
if (error->domain == GTK_BUILDER_ERROR) {
g_warning ("Failed to load popup-menu.ui: %s", error->message);
g_error_free (error);
return NULL;
}
g_error_free (error);
error = NULL;
if (gtk_builder_add_from_file (xml, PKGDATADIR "/popup-menu.ui", &error) == 0) {
g_warning ("Failed to load popup-menu.ui: %s", error->message);
g_error_free (error);
return NULL;
}
}
gtk_builder_connect_signals (xml, NULL);
if (bluetooth_killswitch_has_killswitches (killswitch) != FALSE) {
GObject *object;
object = gtk_builder_get_object (xml, "killswitch-label");
gtk_action_set_visible (GTK_ACTION (object), TRUE);
}
if (option_debug != FALSE) {
GObject *object;
object = gtk_builder_get_object (xml, "quit");
gtk_action_set_visible (GTK_ACTION (object), TRUE);
}
object = gtk_builder_get_object (xml, "bluetooth-applet-ui-manager");
devices_action_group = gtk_action_group_new ("devices-action-group");
gtk_ui_manager_insert_action_group (GTK_UI_MANAGER (object),
devices_action_group, -1);
action_set_bold (GTK_UI_MANAGER (object),
GTK_ACTION (gtk_builder_get_object (xml, "devices-label")),
"/bluetooth-applet-popup/devices-label");
return GTK_WIDGET (gtk_builder_get_object (xml, "bluetooth-applet-popup"));
}
static void
update_menu_items (void)
{
gboolean enabled;
GObject *object;
g_printf("update_menu_items\n");
if (num_adapters_present == 0)
enabled = FALSE;
else
enabled = (num_adapters_present - num_adapters_powered) <= 0;
object = gtk_builder_get_object (xml, "adapter-action-group");
gtk_action_group_set_visible (GTK_ACTION_GROUP (object), enabled);
gtk_action_group_set_visible (devices_action_group, enabled);
if (enabled == FALSE)
return;
gtk_action_group_set_sensitive (GTK_ACTION_GROUP (object), TRUE);
}
static void
update_icon_visibility (void)
{
g_printf("update_icon_visibility\n");
if (num_adapters_powered == 0)
set_icon (FALSE);
else
set_icon (TRUE);
if (show_icon_pref != FALSE) {
if (num_adapters_present > 0 ||
bluetooth_killswitch_has_killswitches (killswitch) != FALSE) {
show_icon ();
return;
}
}
hide_icon ();
}
static void
update_discoverability (GtkTreeIter *iter)
{
gboolean discoverable;
GObject *object;
/* Avoid loops from changing the UI */
if (discover_lock != FALSE)
return;
discover_lock = TRUE;
object = gtk_builder_get_object (xml, "discoverable");
if (iter == NULL) {
gtk_action_set_visible (GTK_ACTION (object), FALSE);
return;
}
gtk_tree_model_get (devices_model, iter,
BLUETOOTH_COLUMN_DISCOVERABLE, &discoverable,
-1);
gtk_action_set_visible (GTK_ACTION (object), TRUE);
gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (object), discoverable);
discover_lock = FALSE;
}
static void
set_device_status_label (const char *address, int connected)
{
char *action_name, *action_path;
GtkAction *status;
GObject *object;
const char *label;
g_return_if_fail (address != NULL);
g_printf("set_device_status_label\n");
switch (connected) {
case DISCONNECTING:
label = N_("Disconnecting...");
break;
case CONNECTING:
label = N_("Connecting...");
break;
case CONNECTED:
label = N_("Connected");
break;
case DISCONNECTED:
label = N_("Disconnected");
break;
default:
g_assert_not_reached ();
}
action_name = g_strdup_printf ("%s-status", address);
status = gtk_action_group_get_action (devices_action_group, action_name);
g_free (action_name);
g_assert (status != NULL);
gtk_action_set_label (status, _(label));
object = gtk_builder_get_object (xml, "bluetooth-applet-ui-manager");
action_path = g_strdup_printf ("/bluetooth-applet-popup/devices-placeholder/%s/%s-status",
address, address);
action_set_bold (GTK_UI_MANAGER (object), status, action_path);
g_free (action_path);
}
static void
connection_action_callback (BluetoothClient *_client,
gboolean success,
gpointer user_data)
{
GtkAction *action = (GtkAction *) user_data;
const char *address;
int connected;
/* Revert to the previous state and wait for the
* BluetoothClient to catch up */
connected = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action), "connected"));
if (connected == DISCONNECTING)
connected = CONNECTED;
else if (connected == CONNECTING)
connected = DISCONNECTED;
else
return;
g_object_set_data_full (G_OBJECT (action),
"connected", GINT_TO_POINTER (connected), NULL);
address = g_object_get_data (G_OBJECT (action), "address");
set_device_status_label (address, connected);
}
static void
on_connect_activate (GtkAction *action, gpointer data)
{
int connected;
const char *path;
const char *address;
connected = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action), "connected"));
/* Don't try connecting if something is already in progress */
if (connected == CONNECTING || connected == DISCONNECTING)
return;
path = g_object_get_data (G_OBJECT (action), "device-path");
if (connected == DISCONNECTED) {
if (bluetooth_client_connect_service (client, path, connection_action_callback, action) != FALSE)
connected = DISCONNECTING;
} else if (connected == CONNECTED) {
if (bluetooth_client_disconnect_service (client, path, connection_action_callback, action) != FALSE)
connected = CONNECTING;
} else {
g_assert_not_reached ();
}
g_object_set_data_full (G_OBJECT (action),
"connected", GINT_TO_POINTER (connected), NULL);
address = g_object_get_data (G_OBJECT (action), "address");
set_device_status_label (address, connected);
}
static void
action_set_bold (GtkUIManager *manager, GtkAction *action, const char *path)
{
GtkWidget *widget;
char *str;
const char *label;
/* That sucks, but otherwise the widget might not exist */
gtk_ui_manager_ensure_update (manager);
widget = gtk_ui_manager_get_widget (manager, path);
g_assert (widget);
label = gtk_action_get_label (action);
str = g_strdup_printf ("<b>%s</b>", label);
gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (widget))), str);
g_free (str);
}
static void
remove_action_item (GtkAction *action, GtkUIManager *manager)
{
guint menu_merge_id;
GList *actions, *l;
g_printf("remove_action_item\n");
menu_merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action), "merge-id"));
gtk_ui_manager_remove_ui (GTK_UI_MANAGER (manager), menu_merge_id);
actions = gtk_action_group_list_actions (devices_action_group);
for (l = actions; l != NULL; l = l->next) {
GtkAction *a = l->data;
/* Don't remove the top-level action straight away */
if (g_str_equal (gtk_action_get_name (a), gtk_action_get_name (action)) != FALSE)
continue;
/* But remove all the sub-actions for it */
if (g_str_has_prefix (gtk_action_get_name (a), gtk_action_get_name (action)) != FALSE)
gtk_action_group_remove_action (devices_action_group, a);
}
g_list_free (actions);
gtk_action_group_remove_action (devices_action_group, action);
}
static char *
escape_label_for_action (const char *alias)
{
char **tokens, *name;
if (strchr (alias, '_') == NULL)
return g_strdup (alias);
tokens = g_strsplit (alias, "_", -1);
name = g_strjoinv ("__", tokens);
g_strfreev (tokens);
return name;
}
static gboolean
device_has_uuid (const char **uuids, const char *uuid)
{
guint i;
if (uuids == NULL)
return FALSE;
for (i = 0; uuids[i] != NULL; i++) {
if (g_str_equal (uuid, uuids[i]) != FALSE)
return TRUE;
}
return FALSE;
}
static gboolean
device_has_submenu (const char **uuids, GHashTable *services, BluetoothType type)
{
if (services != NULL)
return TRUE;
if (device_has_uuid (uuids, "OBEXObjectPush") != FALSE)
return TRUE;
if (device_has_uuid (uuids, "OBEXFileTransfer") != FALSE)
return TRUE;
if (type == BLUETOOTH_TYPE_KEYBOARD ||
type == BLUETOOTH_TYPE_MOUSE)
return TRUE;
return FALSE;
}
static GtkAction *
add_menu_item (const char *address,
const char *suffix,
const char *label,
GtkUIManager *uimanager,
guint merge_id,
GCallback callback)
{
GtkAction *action;
char *action_path, *action_name;
g_return_val_if_fail (address != NULL, NULL);
g_return_val_if_fail (suffix != NULL, NULL);
g_return_val_if_fail (label != NULL, NULL);
g_printf("add_menu_item\n");
action_name = g_strdup_printf ("%s-%s", address, suffix);
action = gtk_action_new (action_name, label, NULL, NULL);
gtk_action_group_add_action (devices_action_group, action);
g_object_unref (action);
action_path = g_strdup_printf ("/bluetooth-applet-popup/devices-placeholder/%s", address);
gtk_ui_manager_add_ui (uimanager, merge_id,
action_path, action_name, action_name,
GTK_UI_MANAGER_MENUITEM, FALSE);
g_free (action_path);
g_free (action_name);
if (callback != NULL)
g_signal_connect (G_OBJECT (action), "activate", callback, NULL);
g_object_set_data_full (G_OBJECT (action),
"address", g_strdup (address), g_free);
return action;
}
static void
add_separator_item (const char *address,
const char *suffix,
GtkUIManager *uimanager,
guint merge_id)
{
char *action_path, *action_name;
g_return_if_fail (address != NULL);
g_return_if_fail (suffix != NULL);
g_printf("add_separator_item\n");
action_name = g_strdup_printf ("%s-%s", address, suffix);
action_path = g_strdup_printf ("/bluetooth-applet-popup/devices-placeholder/%s", address);
gtk_ui_manager_add_ui (uimanager, merge_id,
action_path, action_name, NULL,
GTK_UI_MANAGER_SEPARATOR, FALSE);
g_free (action_path);
g_free (action_name);
}
static void
update_device_list (GtkTreeIter *parent)
{
GtkUIManager *uimanager;
GtkTreeIter iter;
gboolean cont;
guint num_devices;
GList *actions, *l;
g_printf("update_device_list\n");
num_devices = 0;
uimanager = GTK_UI_MANAGER (gtk_builder_get_object (xml, "bluetooth-applet-ui-manager"));
if (parent == NULL) {
/* No default adapter? Remove everything */
actions = gtk_action_group_list_actions (devices_action_group);
g_list_foreach (actions, (GFunc) remove_action_item, uimanager);
g_list_free (actions);
goto done;
}
/* Get a list of actions, and we'll remove the ones with a
* device in the list. We remove the submenu items first */
actions = gtk_action_group_list_actions (devices_action_group);
for (l = actions; l != NULL; l = l->next) {
if (bluetooth_verify_address (gtk_action_get_name (l->data)) == FALSE)
l->data = NULL;
}
actions = g_list_remove_all (actions, NULL);
cont = gtk_tree_model_iter_children (devices_model, &iter, parent);
while (cont) {
GHashTable *services;
DBusGProxy *proxy;
char *alias, *address, **uuids, *name;
gboolean is_connected;
BluetoothType type;
GtkAction *action, *status, *oper;
gtk_tree_model_get (devices_model, &iter,
BLUETOOTH_COLUMN_PROXY, &proxy,
BLUETOOTH_COLUMN_ADDRESS, &address,
BLUETOOTH_COLUMN_SERVICES, &services,
BLUETOOTH_COLUMN_ALIAS, &alias,
BLUETOOTH_COLUMN_UUIDS, &uuids,
BLUETOOTH_COLUMN_TYPE, &type,
-1);
if (device_has_submenu ((const char **) uuids, services, type) == FALSE ||
address == NULL || proxy == NULL || alias == NULL) {
if (proxy != NULL)
g_object_unref (proxy);
if (services != NULL)
g_hash_table_unref (services);
g_strfreev (uuids);
g_free (alias);
g_free (address);
cont = gtk_tree_model_iter_next (devices_model, &iter);
continue;
}
action = gtk_action_group_get_action (devices_action_group, address);
oper = NULL;
status = NULL;
if (action) {
char *action_name;
actions = g_list_remove (actions, action);
action_name = g_strdup_printf ("%s-status", address);
status = gtk_action_group_get_action (devices_action_group, action_name);
g_free (action_name);
action_name = g_strdup_printf ("%s-action", address);
oper = gtk_action_group_get_action (devices_action_group, action_name);
g_free (action_name);
}
/* If one service is connected, then we're connected */
is_connected = FALSE;
if (services != NULL) {
GList *list, *l;
list = g_hash_table_get_values (services);
for (l = list; l != NULL; l = l->next) {
BluetoothStatus val = GPOINTER_TO_INT (l->data);
if (val == BLUETOOTH_STATUS_CONNECTED ||
val == BLUETOOTH_STATUS_PLAYING) {
is_connected = TRUE;
break;
}
}
g_list_free (list);
}
name = escape_label_for_action (alias);
if (action == NULL) {
guint menu_merge_id;
char *action_path;
/* The menu item with descendants */
action = gtk_action_new (address, name, NULL, NULL);
g_printf("\tAdding action: %s\n", name);
gtk_action_group_add_action (devices_action_group, action);
g_object_unref (action);
menu_merge_id = gtk_ui_manager_new_merge_id (uimanager);
gtk_ui_manager_add_ui (uimanager, menu_merge_id,
"/bluetooth-applet-popup/devices-placeholder", address, address,
GTK_UI_MANAGER_MENU, FALSE);
g_object_set_data_full (G_OBJECT (action),
"merge-id", GUINT_TO_POINTER (menu_merge_id), NULL);
/* The status menu item */
status = add_menu_item (address,
"status",
is_connected ? _("Connected") : _("Disconnected"),
uimanager,
menu_merge_id,
NULL);
gtk_action_set_sensitive (status, FALSE);
if (services != NULL) {
action_path = g_strdup_printf ("/bluetooth-applet-popup/devices-placeholder/%s/%s-status",
address, address);
action_set_bold (uimanager, status, action_path);
g_free (action_path);
} else {
gtk_action_set_visible (status, FALSE);
}
/* The connect button */
oper = add_menu_item (address,
"action",
is_connected ? _("Disconnect") : _("Connect"),
uimanager,
menu_merge_id,
G_CALLBACK (on_connect_activate));
if (services == NULL)
gtk_action_set_visible (oper, FALSE);
add_separator_item (address, "connect-sep", uimanager, menu_merge_id);
/* The Send to... button */
if (device_has_uuid ((const char **) uuids, "OBEXObjectPush") != FALSE) {
add_menu_item (address,
"sendto",
_("Send files..."),
uimanager,
menu_merge_id,
G_CALLBACK (sendto_callback));
g_object_set_data_full (G_OBJECT (action),
"alias", g_strdup (alias), g_free);
}
if (device_has_uuid ((const char **) uuids, "OBEXFileTransfer") != FALSE) {
add_menu_item (address,
"browse",
_("Browse files..."),
uimanager,
menu_merge_id,
G_CALLBACK (browse_callback));
}
add_separator_item (address, "files-sep", uimanager, menu_merge_id);
if (type == BLUETOOTH_TYPE_KEYBOARD && program_available (KEYBOARD_PREFS)) {
add_menu_item (address,
"keyboard",
_("Open Keyboard Preferences..."),
uimanager,
menu_merge_id,
G_CALLBACK (keyboard_callback));
}
if (type == BLUETOOTH_TYPE_MOUSE && program_available (MOUSE_PREFS)) {
add_menu_item (address,
"mouse",
_("Open Mouse Preferences..."),
uimanager,
menu_merge_id,
G_CALLBACK (mouse_callback));
}
if ((type == BLUETOOTH_TYPE_HEADSET ||
type == BLUETOOTH_TYPE_HEADPHONES ||
type == BLUETOOTH_TYPE_OTHER_AUDIO) && program_available (SOUND_PREFS)) {
add_menu_item (address,
"sound",
_("Open Sound Preferences..."),
uimanager,
menu_merge_id,
G_CALLBACK (sound_callback));
}
} else {
gtk_action_set_label (action, name);
gtk_action_set_visible (status, services != NULL);
gtk_action_set_visible (oper, services != NULL);
if (services != NULL) {
set_device_status_label (address, is_connected ? CONNECTED : DISCONNECTED);
gtk_action_set_label (oper, is_connected ? _("Disconnect") : _("Connect"));
}
}
g_free (name);
if (oper != NULL) {
g_object_set_data_full (G_OBJECT (oper),
"connected", GINT_TO_POINTER (is_connected ? CONNECTED : DISCONNECTED), NULL);
g_object_set_data_full (G_OBJECT (oper),
"device-path", g_strdup (dbus_g_proxy_get_path (proxy)), g_free);
}
/* And now for the trick of the day */
if (is_connected != FALSE) {
char *path;
path = g_strdup_printf ("/bluetooth-applet-popup/devices-placeholder/%s", address);
action_set_bold (uimanager, action, path);
g_free (path);
}
num_devices++;
if (proxy != NULL)
g_object_unref (proxy);
if (services != NULL)
g_hash_table_unref (services);
g_strfreev (uuids);
g_free (alias);
g_free (address);
cont = gtk_tree_model_iter_next (devices_model, &iter);
}
/* Remove the left-over devices */
g_list_foreach (actions, (GFunc) remove_action_item, uimanager);
g_list_free (actions);
done:
gtk_ui_manager_ensure_update (uimanager);
gtk_action_set_visible (GTK_ACTION (gtk_builder_get_object (xml, "devices-label")),
num_devices > 0);
}
static void device_changed (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *_iter,
gpointer data)
{
GtkTreeIter iter, *default_iter;
gboolean powered, cont;
g_printf("device_changed\n");
default_iter = NULL;
num_adapters_present = num_adapters_powered = 0;
cont = gtk_tree_model_get_iter_first (model, &iter);
while (cont) {
gboolean is_default;
num_adapters_present++;
gtk_tree_model_get (model, &iter,
BLUETOOTH_COLUMN_DEFAULT, &is_default,
BLUETOOTH_COLUMN_POWERED, &powered,
-1);
if (powered)
num_adapters_powered++;
if (is_default && powered)
default_iter = gtk_tree_iter_copy (&iter);
cont = gtk_tree_model_iter_next (model, &iter);
}
update_discoverability (default_iter);
update_icon_visibility ();
update_menu_items ();
update_device_list (default_iter);
if (default_iter != NULL)
gtk_tree_iter_free (default_iter);
}
static void device_added(GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer user_data)
{
g_printf("device_added\n");
device_changed (model, path, iter, user_data);
}
static void device_removed(GtkTreeModel *model,
GtkTreePath *path,
gpointer user_data)
{
g_printf("device_removed\n");
device_changed (model, path, NULL, user_data);
}
static void gconf_callback(GConfClient *client, guint cnxn_id,
GConfEntry *entry, gpointer user_data)
{
GConfValue *value;
value = gconf_entry_get_value(entry);
if (value == NULL)
return;
if (g_str_equal(entry->key, PREF_SHOW_ICON) == TRUE) {
show_icon_pref = gconf_value_get_bool(value);
update_icon_visibility();
return;
}
}
static GOptionEntry options[] = {
{ "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug, N_("Debug"), NULL },
{ NULL },
};
int main(int argc, char *argv[])
{
UniqueApp *app;
#ifdef HAVE_APP_INDICATOR
AppIndicator *indicator;
#else
GtkStatusIcon *statusicon;
#endif /* HAVE_APP_INDICATOR */
GtkWidget *menu;
GConfValue *value;
GOptionContext *context;
GError *error = NULL;
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
textdomain(GETTEXT_PACKAGE);
g_type_init ();
/* Parse command-line options */
context = g_option_context_new (N_("- Bluetooth applet"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
g_print (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
error->message, argv[0]);
g_error_free (error);
return 1;
}
if (option_debug == FALSE) {
app = unique_app_new ("org.gnome.Bluetooth.applet", NULL);
if (unique_app_is_running (app)) {
gdk_notify_startup_complete ();
g_warning ("Applet is already running, exiting");
return 0;
}
} else {
app = NULL;
}
g_set_application_name(_("Bluetooth Applet"));
gtk_window_set_default_icon_name("bluetooth");
killswitch = bluetooth_killswitch_new ();
g_signal_connect (G_OBJECT (killswitch), "state-changed",
G_CALLBACK (killswitch_state_changed), NULL);
menu = create_popupmenu();
client = bluetooth_client_new();
devices_model = bluetooth_client_get_model(client);
g_signal_connect(G_OBJECT(devices_model), "row-inserted",
G_CALLBACK(device_added), NULL);
g_signal_connect(G_OBJECT(devices_model), "row-deleted",
G_CALLBACK(device_removed), NULL);
g_signal_connect (G_OBJECT (devices_model), "row-changed",
G_CALLBACK (device_changed), NULL);
/* Set the default */
device_changed (devices_model, NULL, NULL, NULL);
if (bluetooth_killswitch_has_killswitches (killswitch) != FALSE) {
killswitch_state_changed (killswitch,
bluetooth_killswitch_get_state (killswitch));
}
gconf = gconf_client_get_default();
value = gconf_client_get (gconf, PREF_SHOW_ICON, NULL);
if (value == NULL) {
show_icon_pref = TRUE;
} else {
show_icon_pref = gconf_value_get_bool (value);
gconf_value_free (value);
}
gconf_client_add_dir(gconf, PREF_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
gconf_client_notify_add(gconf, PREF_DIR,
gconf_callback, NULL, NULL, NULL);
#ifdef HAVE_APP_INDICATOR
indicator = init_notification();
app_indicator_set_menu(indicator, GTK_MENU(menu));
#else
statusicon = init_notification();
#endif /* HAVE_APP_INDICATOR */
update_icon_visibility();
#ifndef HAVE_APP_INDICATOR
g_signal_connect(statusicon, "activate",
G_CALLBACK(activate_callback), menu);
g_signal_connect(statusicon, "popup-menu",
G_CALLBACK(popup_callback), menu);
#endif /* HAVE_APP_INDICATOR */
setup_agents();
gtk_main();
gtk_widget_destroy(menu);
g_object_unref(gconf);
cleanup_agents();
cleanup_notification();
g_object_unref(devices_model);
g_object_unref(client);
if (app != NULL)
g_object_unref (app);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment