Get rid of the old nm applet
This commit is contained in:
parent
c0c753d169
commit
81e2e9ed8d
@ -112,7 +112,6 @@ lib/threadframe/Makefile
|
|||||||
services/Makefile
|
services/Makefile
|
||||||
services/presence/Makefile
|
services/presence/Makefile
|
||||||
services/presence2/Makefile
|
services/presence2/Makefile
|
||||||
services/nm/Makefile
|
|
||||||
services/clipboard/Makefile
|
services/clipboard/Makefile
|
||||||
services/datastore/Makefile
|
services/datastore/Makefile
|
||||||
shell/Makefile
|
shell/Makefile
|
||||||
|
@ -21,13 +21,6 @@
|
|||||||
(gtype-id "SUGAR_TYPE_KEY_GRABBER")
|
(gtype-id "SUGAR_TYPE_KEY_GRABBER")
|
||||||
)
|
)
|
||||||
|
|
||||||
(define-object TrayManager
|
|
||||||
(in-module "Sugar")
|
|
||||||
(parent "GObject")
|
|
||||||
(c-name "SugarTrayManager")
|
|
||||||
(gtype-id "SUGAR_TYPE_TRAY_MANAGER")
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-object PushScroller
|
(define-object PushScroller
|
||||||
(in-module "Sugar")
|
(in-module "Sugar")
|
||||||
(parent "GObject")
|
(parent "GObject")
|
||||||
@ -124,60 +117,6 @@
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
;; From sugar-tray-manager.h
|
|
||||||
|
|
||||||
(define-function tray_manager_get_type
|
|
||||||
(c-name "sugar_tray_manager_get_type")
|
|
||||||
(return-type "GType")
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-function tray_manager_check_running
|
|
||||||
(c-name "sugar_tray_manager_check_running")
|
|
||||||
(return-type "gboolean")
|
|
||||||
(parameters
|
|
||||||
'("GdkScreen*" "screen")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-function tray_manager_new
|
|
||||||
(c-name "sugar_tray_manager_new")
|
|
||||||
(is-constructor-of "SugarTrayManager")
|
|
||||||
(return-type "SugarTrayManager*")
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-method manage_screen
|
|
||||||
(of-object "SugarTrayManager")
|
|
||||||
(c-name "sugar_tray_manager_manage_screen")
|
|
||||||
(return-type "gboolean")
|
|
||||||
(parameters
|
|
||||||
'("GdkScreen*" "screen")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-method get_child_title
|
|
||||||
(of-object "SugarTrayManager")
|
|
||||||
(c-name "sugar_tray_manager_get_child_title")
|
|
||||||
(return-type "char*")
|
|
||||||
(parameters
|
|
||||||
'("SugarTrayManagerChild*" "child")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-method set_orientation
|
|
||||||
(of-object "SugarTrayManager")
|
|
||||||
(c-name "sugar_tray_manager_set_orientation")
|
|
||||||
(return-type "none")
|
|
||||||
(parameters
|
|
||||||
'("GtkOrientation" "orientation")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-method get_orientation
|
|
||||||
(of-object "SugarTrayManager")
|
|
||||||
(c-name "sugar_tray_manager_get_orientation")
|
|
||||||
(return-type "GtkOrientation")
|
|
||||||
)
|
|
||||||
|
|
||||||
;; From sugar-push-scroller.h
|
;; From sugar-push-scroller.h
|
||||||
|
|
||||||
(define-function push_scroller_get_type
|
(define-function push_scroller_get_type
|
||||||
|
@ -7,7 +7,6 @@ headers
|
|||||||
#include "sugar-browser.h"
|
#include "sugar-browser.h"
|
||||||
#include "sugar-key-grabber.h"
|
#include "sugar-key-grabber.h"
|
||||||
#include "sugar-address-entry.h"
|
#include "sugar-address-entry.h"
|
||||||
#include "sugar-tray-manager.h"
|
|
||||||
#include "sugar-push-scroller.h"
|
#include "sugar-push-scroller.h"
|
||||||
#include "sugar-download-manager.h"
|
#include "sugar-download-manager.h"
|
||||||
#include "sugar-download.h"
|
#include "sugar-download.h"
|
||||||
@ -22,9 +21,6 @@ extern Pycairo_CAPI_t *Pycairo_CAPI;
|
|||||||
%%
|
%%
|
||||||
modulename gecko
|
modulename gecko
|
||||||
%%
|
%%
|
||||||
ignore
|
|
||||||
sugar_tray_manager_new
|
|
||||||
%%
|
|
||||||
import gobject.GObject as PyGObject_Type
|
import gobject.GObject as PyGObject_Type
|
||||||
import gtk.Entry as PyGtkEntry_Type
|
import gtk.Entry as PyGtkEntry_Type
|
||||||
import gtk.gdk.Screen as PyGdkScreen_Type
|
import gtk.gdk.Screen as PyGdkScreen_Type
|
||||||
|
@ -44,8 +44,6 @@ libsugarprivate_la_SOURCES = \
|
|||||||
sugar-key-grabber.c \
|
sugar-key-grabber.c \
|
||||||
sugar-push-scroller.c \
|
sugar-push-scroller.c \
|
||||||
sugar-push-scroller.h \
|
sugar-push-scroller.h \
|
||||||
sugar-tray-manager.c \
|
|
||||||
sugar-tray-manager.h \
|
|
||||||
sugar-utils.c \
|
sugar-utils.c \
|
||||||
sugar-utils.h
|
sugar-utils.h
|
||||||
|
|
||||||
|
@ -1,908 +0,0 @@
|
|||||||
/* na-tray-manager.c
|
|
||||||
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
|
|
||||||
* Copyright (C) 2003-2006 Vincent Untz
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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 library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* Used to be: eggtraymanager.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <libintl.h>
|
|
||||||
|
|
||||||
#include "sugar-tray-manager.h"
|
|
||||||
|
|
||||||
#include <gdkconfig.h>
|
|
||||||
#include <glib/gi18n.h>
|
|
||||||
#if defined (GDK_WINDOWING_X11)
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
#include <X11/Xatom.h>
|
|
||||||
#elif defined (GDK_WINDOWING_WIN32)
|
|
||||||
#include <gdk/gdkwin32.h>
|
|
||||||
#endif
|
|
||||||
#include <gtk/gtkinvisible.h>
|
|
||||||
#include <gtk/gtksocket.h>
|
|
||||||
#include <gtk/gtkwindow.h>
|
|
||||||
|
|
||||||
#include "sugar-marshal.h"
|
|
||||||
|
|
||||||
/* Signals */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TRAY_ICON_ADDED,
|
|
||||||
TRAY_ICON_REMOVED,
|
|
||||||
MESSAGE_SENT,
|
|
||||||
MESSAGE_CANCELLED,
|
|
||||||
LOST_SELECTION,
|
|
||||||
LAST_SIGNAL
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
PROP_0,
|
|
||||||
PROP_ORIENTATION
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
long id, len;
|
|
||||||
long remaining_len;
|
|
||||||
|
|
||||||
long timeout;
|
|
||||||
char *str;
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
Window window;
|
|
||||||
#endif
|
|
||||||
} PendingMessage;
|
|
||||||
|
|
||||||
static guint manager_signals[LAST_SIGNAL] = { 0 };
|
|
||||||
|
|
||||||
#define SYSTEM_TRAY_REQUEST_DOCK 0
|
|
||||||
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
|
|
||||||
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
|
|
||||||
|
|
||||||
#define SYSTEM_TRAY_ORIENTATION_HORZ 0
|
|
||||||
#define SYSTEM_TRAY_ORIENTATION_VERT 1
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
static gboolean sugar_tray_manager_check_running_screen_x11 (GdkScreen *screen);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void sugar_tray_manager_finalize (GObject *object);
|
|
||||||
static void sugar_tray_manager_set_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
const GValue *value,
|
|
||||||
GParamSpec *pspec);
|
|
||||||
static void sugar_tray_manager_get_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec);
|
|
||||||
|
|
||||||
static void sugar_tray_manager_unmanage (SugarTrayManager *manager);
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (SugarTrayManager, sugar_tray_manager, G_TYPE_OBJECT)
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_init (SugarTrayManager *manager)
|
|
||||||
{
|
|
||||||
manager->invisible = NULL;
|
|
||||||
manager->socket_table = g_hash_table_new (NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_class_init (SugarTrayManagerClass *klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class;
|
|
||||||
|
|
||||||
gobject_class = (GObjectClass *)klass;
|
|
||||||
|
|
||||||
gobject_class->finalize = sugar_tray_manager_finalize;
|
|
||||||
gobject_class->set_property = sugar_tray_manager_set_property;
|
|
||||||
gobject_class->get_property = sugar_tray_manager_get_property;
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class,
|
|
||||||
PROP_ORIENTATION,
|
|
||||||
g_param_spec_enum ("orientation",
|
|
||||||
_("Orientation"),
|
|
||||||
_("The orientation of the tray."),
|
|
||||||
GTK_TYPE_ORIENTATION,
|
|
||||||
GTK_ORIENTATION_HORIZONTAL,
|
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
||||||
|
|
||||||
manager_signals[TRAY_ICON_ADDED] =
|
|
||||||
g_signal_new ("tray_icon_added",
|
|
||||||
G_OBJECT_CLASS_TYPE (klass),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET (SugarTrayManagerClass, tray_icon_added),
|
|
||||||
NULL, NULL,
|
|
||||||
g_cclosure_marshal_VOID__OBJECT,
|
|
||||||
G_TYPE_NONE, 1,
|
|
||||||
GTK_TYPE_SOCKET);
|
|
||||||
|
|
||||||
manager_signals[TRAY_ICON_REMOVED] =
|
|
||||||
g_signal_new ("tray_icon_removed",
|
|
||||||
G_OBJECT_CLASS_TYPE (klass),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET (SugarTrayManagerClass, tray_icon_removed),
|
|
||||||
NULL, NULL,
|
|
||||||
g_cclosure_marshal_VOID__OBJECT,
|
|
||||||
G_TYPE_NONE, 1,
|
|
||||||
GTK_TYPE_SOCKET);
|
|
||||||
manager_signals[MESSAGE_SENT] =
|
|
||||||
g_signal_new ("message_sent",
|
|
||||||
G_OBJECT_CLASS_TYPE (klass),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET (SugarTrayManagerClass, message_sent),
|
|
||||||
NULL, NULL,
|
|
||||||
sugar_marshal_VOID__OBJECT_STRING_LONG_LONG,
|
|
||||||
G_TYPE_NONE, 4,
|
|
||||||
GTK_TYPE_SOCKET,
|
|
||||||
G_TYPE_STRING,
|
|
||||||
G_TYPE_LONG,
|
|
||||||
G_TYPE_LONG);
|
|
||||||
manager_signals[MESSAGE_CANCELLED] =
|
|
||||||
g_signal_new ("message_cancelled",
|
|
||||||
G_OBJECT_CLASS_TYPE (klass),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET (SugarTrayManagerClass, message_cancelled),
|
|
||||||
NULL, NULL,
|
|
||||||
sugar_marshal_VOID__OBJECT_LONG,
|
|
||||||
G_TYPE_NONE, 2,
|
|
||||||
GTK_TYPE_SOCKET,
|
|
||||||
G_TYPE_LONG);
|
|
||||||
manager_signals[LOST_SELECTION] =
|
|
||||||
g_signal_new ("lost_selection",
|
|
||||||
G_OBJECT_CLASS_TYPE (klass),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET (SugarTrayManagerClass, lost_selection),
|
|
||||||
NULL, NULL,
|
|
||||||
g_cclosure_marshal_VOID__VOID,
|
|
||||||
G_TYPE_NONE, 0);
|
|
||||||
|
|
||||||
#if defined (GDK_WINDOWING_X11)
|
|
||||||
/* Nothing */
|
|
||||||
#elif defined (GDK_WINDOWING_WIN32)
|
|
||||||
g_warning ("Port SugarTrayManager to Win32");
|
|
||||||
#else
|
|
||||||
g_warning ("Port SugarTrayManager to this GTK+ backend");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_finalize (GObject *object)
|
|
||||||
{
|
|
||||||
SugarTrayManager *manager;
|
|
||||||
|
|
||||||
manager = SUGAR_TRAY_MANAGER (object);
|
|
||||||
|
|
||||||
sugar_tray_manager_unmanage (manager);
|
|
||||||
|
|
||||||
g_list_free (manager->messages);
|
|
||||||
g_hash_table_destroy (manager->socket_table);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (sugar_tray_manager_parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_set_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
const GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
SugarTrayManager *manager = SUGAR_TRAY_MANAGER (object);
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
case PROP_ORIENTATION:
|
|
||||||
sugar_tray_manager_set_orientation (manager, g_value_get_enum (value));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_get_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
SugarTrayManager *manager = SUGAR_TRAY_MANAGER (object);
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
case PROP_ORIENTATION:
|
|
||||||
g_value_set_enum (value, manager->orientation);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SugarTrayManager *
|
|
||||||
sugar_tray_manager_new (void)
|
|
||||||
{
|
|
||||||
SugarTrayManager *manager;
|
|
||||||
|
|
||||||
manager = g_object_new (SUGAR_TYPE_TRAY_MANAGER, NULL);
|
|
||||||
|
|
||||||
return manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
sugar_tray_manager_plug_removed (GtkSocket *socket,
|
|
||||||
SugarTrayManager *manager)
|
|
||||||
{
|
|
||||||
Window *window;
|
|
||||||
|
|
||||||
window = g_object_get_data (G_OBJECT (socket), "na-tray-child-window");
|
|
||||||
|
|
||||||
g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (*window));
|
|
||||||
g_object_set_data (G_OBJECT (socket), "na-tray-child-window",
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, socket);
|
|
||||||
|
|
||||||
/* This destroys the socket. */
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_make_socket_transparent (GtkWidget *widget,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
if (GTK_WIDGET_NO_WINDOW (widget))
|
|
||||||
return;
|
|
||||||
|
|
||||||
gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
sugar_tray_manager_socket_exposed (GtkWidget *widget,
|
|
||||||
GdkEventExpose *event,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
gdk_window_clear_area (widget->window,
|
|
||||||
event->area.x, event->area.y,
|
|
||||||
event->area.width, event->area.height);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_socket_style_set (GtkWidget *widget,
|
|
||||||
GtkStyle *previous_style,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
if (widget->window == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
sugar_tray_manager_make_socket_transparent (widget, user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_handle_dock_request (SugarTrayManager *manager,
|
|
||||||
XClientMessageEvent *xevent)
|
|
||||||
{
|
|
||||||
GtkWidget *socket;
|
|
||||||
Window *window;
|
|
||||||
GtkRequisition req;
|
|
||||||
|
|
||||||
if (g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->data.l[2])))
|
|
||||||
{
|
|
||||||
/* We already got this notification earlier, ignore this one */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket = gtk_socket_new ();
|
|
||||||
|
|
||||||
gtk_widget_set_app_paintable (socket, TRUE);
|
|
||||||
//FIXME: need to find a theme where this (and expose event) is needed
|
|
||||||
gtk_widget_set_double_buffered (socket, FALSE);
|
|
||||||
|
|
||||||
/* FIXME Disabled this so that I can customize the icons background in theme.
|
|
||||||
I couldn't find a way to set a specific color for the GtkPlug.
|
|
||||||
g_signal_connect (socket, "realize",
|
|
||||||
G_CALLBACK (sugar_tray_manager_make_socket_transparent), NULL);
|
|
||||||
*/
|
|
||||||
|
|
||||||
g_signal_connect (socket, "expose_event",
|
|
||||||
G_CALLBACK (sugar_tray_manager_socket_exposed), NULL);
|
|
||||||
g_signal_connect_after (socket, "style_set",
|
|
||||||
G_CALLBACK (sugar_tray_manager_socket_style_set), NULL);
|
|
||||||
|
|
||||||
/* We need to set the child window here
|
|
||||||
* so that the client can call _get functions
|
|
||||||
* in the signal handler
|
|
||||||
*/
|
|
||||||
window = g_new (Window, 1);
|
|
||||||
*window = xevent->data.l[2];
|
|
||||||
|
|
||||||
g_object_set_data_full (G_OBJECT (socket),
|
|
||||||
"na-tray-child-window",
|
|
||||||
window, g_free);
|
|
||||||
g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
|
|
||||||
socket);
|
|
||||||
|
|
||||||
/* Add the socket only if it's been attached */
|
|
||||||
if (GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))))
|
|
||||||
{
|
|
||||||
g_signal_connect (socket, "plug_removed",
|
|
||||||
G_CALLBACK (sugar_tray_manager_plug_removed), manager);
|
|
||||||
|
|
||||||
gtk_socket_add_id (GTK_SOCKET (socket), *window);
|
|
||||||
|
|
||||||
g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (*window), socket);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure the icons have a meaningfull size ...
|
|
||||||
*/
|
|
||||||
req.width = req.height = 1;
|
|
||||||
gtk_widget_size_request (socket, &req);
|
|
||||||
/*
|
|
||||||
if ((req.width < 16) || (req.height < 16))
|
|
||||||
{
|
|
||||||
gint nw = MAX (24, req.width);
|
|
||||||
gint nh = MAX (24, req.height);
|
|
||||||
g_warning (_("tray icon has requested a size of (%i x %i), resizing to (%i x %i)"),
|
|
||||||
req.width, req.height, nw, nh);
|
|
||||||
gtk_widget_set_size_request(icon, nw, nh);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
gtk_widget_show(socket);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
gtk_widget_destroy (socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
pending_message_free (PendingMessage *message)
|
|
||||||
{
|
|
||||||
g_free (message->str);
|
|
||||||
g_free (message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GdkFilterReturn
|
|
||||||
sugar_tray_manager_handle_client_message_message_data (GdkXEvent *xev,
|
|
||||||
GdkEvent *event,
|
|
||||||
gpointer data)
|
|
||||||
{
|
|
||||||
XClientMessageEvent *xevent;
|
|
||||||
SugarTrayManager *manager;
|
|
||||||
GList *p;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
xevent = (XClientMessageEvent *) xev;
|
|
||||||
manager = data;
|
|
||||||
|
|
||||||
/* Try to see if we can find the pending message in the list */
|
|
||||||
for (p = manager->messages; p; p = p->next)
|
|
||||||
{
|
|
||||||
PendingMessage *msg = p->data;
|
|
||||||
|
|
||||||
if (xevent->window == msg->window)
|
|
||||||
{
|
|
||||||
/* Append the message */
|
|
||||||
len = MIN (msg->remaining_len, 20);
|
|
||||||
|
|
||||||
memcpy ((msg->str + msg->len - msg->remaining_len),
|
|
||||||
&xevent->data, len);
|
|
||||||
msg->remaining_len -= len;
|
|
||||||
|
|
||||||
if (msg->remaining_len == 0)
|
|
||||||
{
|
|
||||||
GtkSocket *socket;
|
|
||||||
|
|
||||||
socket = g_hash_table_lookup (manager->socket_table,
|
|
||||||
GINT_TO_POINTER (msg->window));
|
|
||||||
|
|
||||||
if (socket)
|
|
||||||
g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
|
|
||||||
socket, msg->str, msg->id, msg->timeout);
|
|
||||||
|
|
||||||
pending_message_free (msg);
|
|
||||||
manager->messages = g_list_remove_link (manager->messages, p);
|
|
||||||
g_list_free_1 (p);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GDK_FILTER_REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_handle_begin_message (SugarTrayManager *manager,
|
|
||||||
XClientMessageEvent *xevent)
|
|
||||||
{
|
|
||||||
GtkSocket *socket;
|
|
||||||
GList *p;
|
|
||||||
PendingMessage *msg;
|
|
||||||
long timeout;
|
|
||||||
long len;
|
|
||||||
long id;
|
|
||||||
|
|
||||||
socket = g_hash_table_lookup (manager->socket_table,
|
|
||||||
GINT_TO_POINTER (xevent->window));
|
|
||||||
/* we don't know about this tray icon, so ignore the message */
|
|
||||||
if (!socket)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Check if the same message is already in the queue and remove it if so */
|
|
||||||
for (p = manager->messages; p; p = p->next)
|
|
||||||
{
|
|
||||||
PendingMessage *msg = p->data;
|
|
||||||
|
|
||||||
if (xevent->window == msg->window &&
|
|
||||||
xevent->data.l[4] == msg->id)
|
|
||||||
{
|
|
||||||
/* Hmm, we found it, now remove it */
|
|
||||||
pending_message_free (msg);
|
|
||||||
manager->messages = g_list_remove_link (manager->messages, p);
|
|
||||||
g_list_free_1 (p);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout = xevent->data.l[2];
|
|
||||||
len = xevent->data.l[3];
|
|
||||||
id = xevent->data.l[4];
|
|
||||||
|
|
||||||
if (len == 0)
|
|
||||||
{
|
|
||||||
g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
|
|
||||||
socket, "", id, timeout);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Now add the new message to the queue */
|
|
||||||
msg = g_new0 (PendingMessage, 1);
|
|
||||||
msg->window = xevent->window;
|
|
||||||
msg->timeout = timeout;
|
|
||||||
msg->len = len;
|
|
||||||
msg->id = id;
|
|
||||||
msg->remaining_len = msg->len;
|
|
||||||
msg->str = g_malloc (msg->len + 1);
|
|
||||||
msg->str[msg->len] = '\0';
|
|
||||||
manager->messages = g_list_prepend (manager->messages, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_handle_cancel_message (SugarTrayManager *manager,
|
|
||||||
XClientMessageEvent *xevent)
|
|
||||||
{
|
|
||||||
GList *p;
|
|
||||||
GtkSocket *socket;
|
|
||||||
|
|
||||||
/* Check if the message is in the queue and remove it if so */
|
|
||||||
for (p = manager->messages; p; p = p->next)
|
|
||||||
{
|
|
||||||
PendingMessage *msg = p->data;
|
|
||||||
|
|
||||||
if (xevent->window == msg->window &&
|
|
||||||
xevent->data.l[4] == msg->id)
|
|
||||||
{
|
|
||||||
pending_message_free (msg);
|
|
||||||
manager->messages = g_list_remove_link (manager->messages, p);
|
|
||||||
g_list_free_1 (p);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
socket = g_hash_table_lookup (manager->socket_table,
|
|
||||||
GINT_TO_POINTER (xevent->window));
|
|
||||||
|
|
||||||
if (socket)
|
|
||||||
{
|
|
||||||
g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
|
|
||||||
socket, xevent->data.l[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static GdkFilterReturn
|
|
||||||
sugar_tray_manager_handle_client_message_opcode (GdkXEvent *xev,
|
|
||||||
GdkEvent *event,
|
|
||||||
gpointer data)
|
|
||||||
{
|
|
||||||
XClientMessageEvent *xevent;
|
|
||||||
SugarTrayManager *manager;
|
|
||||||
|
|
||||||
xevent = (XClientMessageEvent *) xev;
|
|
||||||
manager = data;
|
|
||||||
|
|
||||||
switch (xevent->data.l[1])
|
|
||||||
{
|
|
||||||
case SYSTEM_TRAY_REQUEST_DOCK:
|
|
||||||
/* Ignore this one since we don't know on which window this was received
|
|
||||||
* and so we can't know for which screen this is. It will be handled
|
|
||||||
* in sugar_tray_manager_window_filter() since we also receive it there */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYSTEM_TRAY_BEGIN_MESSAGE:
|
|
||||||
sugar_tray_manager_handle_begin_message (manager, xevent);
|
|
||||||
return GDK_FILTER_REMOVE;
|
|
||||||
|
|
||||||
case SYSTEM_TRAY_CANCEL_MESSAGE:
|
|
||||||
sugar_tray_manager_handle_cancel_message (manager, xevent);
|
|
||||||
return GDK_FILTER_REMOVE;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GDK_FILTER_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GdkFilterReturn
|
|
||||||
sugar_tray_manager_window_filter (GdkXEvent *xev,
|
|
||||||
GdkEvent *event,
|
|
||||||
gpointer data)
|
|
||||||
{
|
|
||||||
XEvent *xevent = (GdkXEvent *)xev;
|
|
||||||
SugarTrayManager *manager = data;
|
|
||||||
|
|
||||||
if (xevent->type == ClientMessage)
|
|
||||||
{
|
|
||||||
/* We handle this client message here. See comment in
|
|
||||||
* sugar_tray_manager_handle_client_message_opcode() for details */
|
|
||||||
if (xevent->xclient.message_type == manager->opcode_atom &&
|
|
||||||
xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK)
|
|
||||||
{
|
|
||||||
sugar_tray_manager_handle_dock_request (manager,
|
|
||||||
(XClientMessageEvent *) xevent);
|
|
||||||
return GDK_FILTER_REMOVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (xevent->type == SelectionClear)
|
|
||||||
{
|
|
||||||
g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
|
|
||||||
sugar_tray_manager_unmanage (manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GDK_FILTER_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
//FIXME investigate why this doesn't work
|
|
||||||
static gboolean
|
|
||||||
sugar_tray_manager_selection_clear_event (GtkWidget *widget,
|
|
||||||
GdkEventSelection *event,
|
|
||||||
SugarTrayManager *manager)
|
|
||||||
{
|
|
||||||
g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
|
|
||||||
sugar_tray_manager_unmanage (manager);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_unmanage (SugarTrayManager *manager)
|
|
||||||
{
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
GdkDisplay *display;
|
|
||||||
guint32 timestamp;
|
|
||||||
GtkWidget *invisible;
|
|
||||||
|
|
||||||
if (manager->invisible == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
invisible = manager->invisible;
|
|
||||||
g_assert (GTK_IS_INVISIBLE (invisible));
|
|
||||||
g_assert (GTK_WIDGET_REALIZED (invisible));
|
|
||||||
g_assert (GDK_IS_WINDOW (invisible->window));
|
|
||||||
|
|
||||||
display = gtk_widget_get_display (invisible);
|
|
||||||
|
|
||||||
if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
|
|
||||||
invisible->window)
|
|
||||||
{
|
|
||||||
timestamp = gdk_x11_get_server_time (invisible->window);
|
|
||||||
gdk_selection_owner_set_for_display (display,
|
|
||||||
NULL,
|
|
||||||
manager->selection_atom,
|
|
||||||
timestamp,
|
|
||||||
TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
//FIXME: we should also use gdk_remove_client_message_filter when it's
|
|
||||||
//available
|
|
||||||
// See bug #351254
|
|
||||||
gdk_window_remove_filter (invisible->window,
|
|
||||||
sugar_tray_manager_window_filter, manager);
|
|
||||||
|
|
||||||
manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */
|
|
||||||
gtk_widget_destroy (invisible);
|
|
||||||
g_object_unref (G_OBJECT (invisible));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sugar_tray_manager_set_orientation_property (SugarTrayManager *manager)
|
|
||||||
{
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
GdkDisplay *display;
|
|
||||||
Atom orientation_atom;
|
|
||||||
gulong data[1];
|
|
||||||
|
|
||||||
if (!manager->invisible || !manager->invisible->window)
|
|
||||||
return;
|
|
||||||
|
|
||||||
display = gtk_widget_get_display (manager->invisible);
|
|
||||||
orientation_atom = gdk_x11_get_xatom_by_name_for_display (display,
|
|
||||||
"_NET_SYSTEM_TRAY_ORIENTATION");
|
|
||||||
|
|
||||||
data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ?
|
|
||||||
SYSTEM_TRAY_ORIENTATION_HORZ :
|
|
||||||
SYSTEM_TRAY_ORIENTATION_VERT;
|
|
||||||
|
|
||||||
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
|
|
||||||
GDK_WINDOW_XWINDOW (manager->invisible->window),
|
|
||||||
orientation_atom,
|
|
||||||
XA_CARDINAL, 32,
|
|
||||||
PropModeReplace,
|
|
||||||
(guchar *) &data, 1);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
sugar_tray_manager_manage_screen_x11 (SugarTrayManager *manager,
|
|
||||||
GdkScreen *screen)
|
|
||||||
{
|
|
||||||
GdkDisplay *display;
|
|
||||||
Screen *xscreen;
|
|
||||||
GtkWidget *invisible;
|
|
||||||
char *selection_atom_name;
|
|
||||||
guint32 timestamp;
|
|
||||||
|
|
||||||
g_return_val_if_fail (SUGAR_IS_TRAY_MANAGER (manager), FALSE);
|
|
||||||
g_return_val_if_fail (manager->screen == NULL, FALSE);
|
|
||||||
|
|
||||||
/* If there's already a manager running on the screen
|
|
||||||
* we can't create another one.
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
if (sugar_tray_manager_check_running_screen_x11 (screen))
|
|
||||||
return FALSE;
|
|
||||||
#endif
|
|
||||||
display = gdk_screen_get_display (screen);
|
|
||||||
xscreen = GDK_SCREEN_XSCREEN (screen);
|
|
||||||
|
|
||||||
invisible = gtk_invisible_new_for_screen (screen);
|
|
||||||
gtk_widget_realize (invisible);
|
|
||||||
|
|
||||||
gtk_widget_add_events (invisible,
|
|
||||||
GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
|
|
||||||
|
|
||||||
selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
|
|
||||||
gdk_screen_get_number (screen));
|
|
||||||
manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE);
|
|
||||||
g_free (selection_atom_name);
|
|
||||||
|
|
||||||
sugar_tray_manager_set_orientation_property (manager);
|
|
||||||
|
|
||||||
timestamp = gdk_x11_get_server_time (invisible->window);
|
|
||||||
|
|
||||||
/* Check if we could set the selection owner successfully */
|
|
||||||
if (gdk_selection_owner_set_for_display (display,
|
|
||||||
invisible->window,
|
|
||||||
manager->selection_atom,
|
|
||||||
timestamp,
|
|
||||||
TRUE))
|
|
||||||
{
|
|
||||||
XClientMessageEvent xev;
|
|
||||||
GdkAtom opcode_atom;
|
|
||||||
GdkAtom message_data_atom;
|
|
||||||
|
|
||||||
xev.type = ClientMessage;
|
|
||||||
xev.window = RootWindowOfScreen (xscreen);
|
|
||||||
xev.message_type = gdk_x11_get_xatom_by_name_for_display (display,
|
|
||||||
"MANAGER");
|
|
||||||
|
|
||||||
xev.format = 32;
|
|
||||||
xev.data.l[0] = timestamp;
|
|
||||||
xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
|
|
||||||
manager->selection_atom);
|
|
||||||
xev.data.l[2] = GDK_WINDOW_XWINDOW (invisible->window);
|
|
||||||
xev.data.l[3] = 0; /* manager specific data */
|
|
||||||
xev.data.l[4] = 0; /* manager specific data */
|
|
||||||
|
|
||||||
XSendEvent (GDK_DISPLAY_XDISPLAY (display),
|
|
||||||
RootWindowOfScreen (xscreen),
|
|
||||||
False, StructureNotifyMask, (XEvent *)&xev);
|
|
||||||
|
|
||||||
manager->invisible = invisible;
|
|
||||||
g_object_ref (G_OBJECT (manager->invisible));
|
|
||||||
|
|
||||||
opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
|
|
||||||
manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display,
|
|
||||||
opcode_atom);
|
|
||||||
|
|
||||||
message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA",
|
|
||||||
FALSE);
|
|
||||||
|
|
||||||
/* Add a window filter */
|
|
||||||
#if 0
|
|
||||||
/* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */
|
|
||||||
g_signal_connect (invisible, "selection-clear-event",
|
|
||||||
G_CALLBACK (sugar_tray_manager_selection_clear_event),
|
|
||||||
manager);
|
|
||||||
#endif
|
|
||||||
/* This is for SYSTEM_TRAY_REQUEST_DOCK and SelectionClear */
|
|
||||||
gdk_window_add_filter (invisible->window,
|
|
||||||
sugar_tray_manager_window_filter, manager);
|
|
||||||
/* This is for SYSTEM_TRAY_BEGIN_MESSAGE and SYSTEM_TRAY_CANCEL_MESSAGE */
|
|
||||||
gdk_display_add_client_message_filter (display, opcode_atom,
|
|
||||||
sugar_tray_manager_handle_client_message_opcode,
|
|
||||||
manager);
|
|
||||||
/* This is for _NET_SYSTEM_TRAY_MESSAGE_DATA */
|
|
||||||
gdk_display_add_client_message_filter (display, message_data_atom,
|
|
||||||
sugar_tray_manager_handle_client_message_message_data,
|
|
||||||
manager);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gtk_widget_destroy (invisible);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
sugar_tray_manager_manage_screen (SugarTrayManager *manager,
|
|
||||||
GdkScreen *screen)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
|
|
||||||
g_return_val_if_fail (manager->screen == NULL, FALSE);
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
return sugar_tray_manager_manage_screen_x11 (manager, screen);
|
|
||||||
#else
|
|
||||||
return FALSE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
sugar_tray_manager_check_running_screen_x11 (GdkScreen *screen)
|
|
||||||
{
|
|
||||||
GdkDisplay *display;
|
|
||||||
Atom selection_atom;
|
|
||||||
char *selection_atom_name;
|
|
||||||
|
|
||||||
display = gdk_screen_get_display (screen);
|
|
||||||
selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
|
|
||||||
gdk_screen_get_number (screen));
|
|
||||||
selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
|
|
||||||
selection_atom_name);
|
|
||||||
g_free (selection_atom_name);
|
|
||||||
|
|
||||||
if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
|
|
||||||
selection_atom) != None)
|
|
||||||
return TRUE;
|
|
||||||
else
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
sugar_tray_manager_check_running (GdkScreen *screen)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
return sugar_tray_manager_check_running_screen_x11 (screen);
|
|
||||||
#else
|
|
||||||
return FALSE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
sugar_tray_manager_get_child_title (SugarTrayManager *manager,
|
|
||||||
SugarTrayManagerChild *child)
|
|
||||||
{
|
|
||||||
char *retval = NULL;
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
GdkDisplay *display;
|
|
||||||
Window *child_window;
|
|
||||||
Atom utf8_string, atom, type;
|
|
||||||
int result;
|
|
||||||
int format;
|
|
||||||
gulong nitems;
|
|
||||||
gulong bytes_after;
|
|
||||||
guchar *val;
|
|
||||||
|
|
||||||
g_return_val_if_fail (SUGAR_IS_TRAY_MANAGER (manager), NULL);
|
|
||||||
g_return_val_if_fail (GTK_IS_SOCKET (child), NULL);
|
|
||||||
|
|
||||||
display = gdk_screen_get_display (manager->screen);
|
|
||||||
|
|
||||||
child_window = g_object_get_data (G_OBJECT (child),
|
|
||||||
"na-tray-child-window");
|
|
||||||
|
|
||||||
utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
|
|
||||||
atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
|
|
||||||
|
|
||||||
gdk_error_trap_push ();
|
|
||||||
|
|
||||||
result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
|
|
||||||
*child_window,
|
|
||||||
atom,
|
|
||||||
0, G_MAXLONG,
|
|
||||||
False, utf8_string,
|
|
||||||
&type, &format, &nitems,
|
|
||||||
&bytes_after, (guchar **)&val);
|
|
||||||
|
|
||||||
if (gdk_error_trap_pop () || result != Success)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (type != utf8_string ||
|
|
||||||
format != 8 ||
|
|
||||||
nitems == 0)
|
|
||||||
{
|
|
||||||
if (val)
|
|
||||||
XFree (val);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!g_utf8_validate (val, nitems, NULL))
|
|
||||||
{
|
|
||||||
XFree (val);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = g_strndup (val, nitems);
|
|
||||||
|
|
||||||
XFree (val);
|
|
||||||
#endif
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
sugar_tray_manager_set_orientation (SugarTrayManager *manager,
|
|
||||||
GtkOrientation orientation)
|
|
||||||
{
|
|
||||||
g_return_if_fail (SUGAR_IS_TRAY_MANAGER (manager));
|
|
||||||
|
|
||||||
if (manager->orientation != orientation)
|
|
||||||
{
|
|
||||||
manager->orientation = orientation;
|
|
||||||
|
|
||||||
sugar_tray_manager_set_orientation_property (manager);
|
|
||||||
|
|
||||||
g_object_notify (G_OBJECT (manager), "orientation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkOrientation
|
|
||||||
sugar_tray_manager_get_orientation (SugarTrayManager *manager)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (SUGAR_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
|
|
||||||
|
|
||||||
return manager->orientation;
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
||||||
/* na-tray-manager.h
|
|
||||||
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
|
|
||||||
* Copyright (C) 2003-2006 Vincent Untz
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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 library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* Used to be: eggtraymanager.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SUGAR_TRAY_MANAGER_H__
|
|
||||||
#define __SUGAR_TRAY_MANAGER_H__
|
|
||||||
|
|
||||||
#include <gtk/gtkwidget.h>
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define SUGAR_TYPE_TRAY_MANAGER (sugar_tray_manager_get_type ())
|
|
||||||
#define SUGAR_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SUGAR_TYPE_TRAY_MANAGER, SugarTrayManager))
|
|
||||||
#define SUGAR_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SUGAR_TYPE_TRAY_MANAGER, SugarTrayManagerClass))
|
|
||||||
#define SUGAR_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SUGAR_TYPE_TRAY_MANAGER))
|
|
||||||
#define SUGAR_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SUGAR_TYPE_TRAY_MANAGER))
|
|
||||||
#define SUGAR_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SUGAR_TYPE_TRAY_MANAGER, SugarTrayManagerClass))
|
|
||||||
|
|
||||||
typedef struct _SugarTrayManager SugarTrayManager;
|
|
||||||
typedef struct _SugarTrayManagerClass SugarTrayManagerClass;
|
|
||||||
typedef struct _SugarTrayManagerChild SugarTrayManagerChild;
|
|
||||||
|
|
||||||
struct _SugarTrayManager
|
|
||||||
{
|
|
||||||
GObject parent_instance;
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
GdkAtom selection_atom;
|
|
||||||
Atom opcode_atom;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GtkWidget *invisible;
|
|
||||||
GdkScreen *screen;
|
|
||||||
GtkOrientation orientation;
|
|
||||||
|
|
||||||
GList *messages;
|
|
||||||
GHashTable *socket_table;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _SugarTrayManagerClass
|
|
||||||
{
|
|
||||||
GObjectClass parent_class;
|
|
||||||
|
|
||||||
void (* tray_icon_added) (SugarTrayManager *manager,
|
|
||||||
SugarTrayManagerChild *child);
|
|
||||||
void (* tray_icon_removed) (SugarTrayManager *manager,
|
|
||||||
SugarTrayManagerChild *child);
|
|
||||||
|
|
||||||
void (* message_sent) (SugarTrayManager *manager,
|
|
||||||
SugarTrayManagerChild *child,
|
|
||||||
const gchar *message,
|
|
||||||
glong id,
|
|
||||||
glong timeout);
|
|
||||||
|
|
||||||
void (* message_cancelled) (SugarTrayManager *manager,
|
|
||||||
SugarTrayManagerChild *child,
|
|
||||||
glong id);
|
|
||||||
|
|
||||||
void (* lost_selection) (SugarTrayManager *manager);
|
|
||||||
};
|
|
||||||
|
|
||||||
GType sugar_tray_manager_get_type (void);
|
|
||||||
|
|
||||||
gboolean sugar_tray_manager_check_running (GdkScreen *screen);
|
|
||||||
SugarTrayManager *sugar_tray_manager_new (void);
|
|
||||||
gboolean sugar_tray_manager_manage_screen (SugarTrayManager *manager,
|
|
||||||
GdkScreen *screen);
|
|
||||||
char *sugar_tray_manager_get_child_title (SugarTrayManager *manager,
|
|
||||||
SugarTrayManagerChild *child);
|
|
||||||
void sugar_tray_manager_set_orientation (SugarTrayManager *manager,
|
|
||||||
GtkOrientation orientation);
|
|
||||||
GtkOrientation sugar_tray_manager_get_orientation (SugarTrayManager *manager);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __SUGAR_TRAY_MANAGER_H__ */
|
|
@ -1 +1 @@
|
|||||||
SUBDIRS = presence presence2 nm clipboard datastore console
|
SUBDIRS = presence presence2 clipboard datastore console
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
sugardir = $(pkgdatadir)/services/nm
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
nmclient.py \
|
|
||||||
nminfo.py \
|
|
||||||
bubble.py \
|
|
||||||
style.py \
|
|
||||||
wepkeydialog.py
|
|
||||||
|
|
||||||
bin_SCRIPTS = sugar-nm-applet
|
|
||||||
|
|
||||||
dbusservicedir = $(sysconfdir)/dbus-1/system.d/
|
|
||||||
dbusservice_DATA = NetworkManagerInfo.conf
|
|
||||||
|
|
||||||
EXTRA_DIST = $(dbusservice_DATA) $(bin_SCRIPTS)
|
|
@ -1,26 +0,0 @@
|
|||||||
<!DOCTYPE busconfig PUBLIC
|
|
||||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
|
||||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
|
||||||
<busconfig>
|
|
||||||
<policy user="root">
|
|
||||||
<allow own="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
<allow send_interface="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
</policy>
|
|
||||||
<policy at_console="true">
|
|
||||||
<allow own="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
<allow send_interface="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
</policy>
|
|
||||||
<policy context="default">
|
|
||||||
<deny own="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
|
|
||||||
<deny send_destination="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
<deny send_interface="org.freedesktop.NetworkManagerInfo"/>
|
|
||||||
</policy>
|
|
||||||
|
|
||||||
<limit name="max_replies_per_connection">512</limit>
|
|
||||||
</busconfig>
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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 library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
import gobject
|
|
||||||
import gtk
|
|
||||||
import hippo
|
|
||||||
|
|
||||||
class Bubble(hippo.CanvasBox, hippo.CanvasItem):
|
|
||||||
__gtype_name__ = 'NetworkBubble'
|
|
||||||
|
|
||||||
__gproperties__ = {
|
|
||||||
'fill-color': (object, None, None,
|
|
||||||
gobject.PARAM_READWRITE),
|
|
||||||
'stroke-color': (object, None, None,
|
|
||||||
gobject.PARAM_READWRITE),
|
|
||||||
'progress-color': (object, None, None,
|
|
||||||
gobject.PARAM_READWRITE),
|
|
||||||
'percent' : (object, None, None,
|
|
||||||
gobject.PARAM_READWRITE),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self._stroke_color = 0xFFFFFFFF
|
|
||||||
self._fill_color = 0xFFFFFFFF
|
|
||||||
self._progress_color = 0x000000FF
|
|
||||||
self._percent = 0
|
|
||||||
self._radius = 8
|
|
||||||
|
|
||||||
hippo.CanvasBox.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
def do_set_property(self, pspec, value):
|
|
||||||
if pspec.name == 'fill-color':
|
|
||||||
self._fill_color = value
|
|
||||||
self.emit_paint_needed(0, 0, -1, -1)
|
|
||||||
elif pspec.name == 'stroke-color':
|
|
||||||
self._stroke_color = value
|
|
||||||
self.emit_paint_needed(0, 0, -1, -1)
|
|
||||||
elif pspec.name == 'progress-color':
|
|
||||||
self._progress_color = value
|
|
||||||
self.emit_paint_needed(0, 0, -1, -1)
|
|
||||||
elif pspec.name == 'percent':
|
|
||||||
self._percent = value
|
|
||||||
self.emit_paint_needed(0, 0, -1, -1)
|
|
||||||
|
|
||||||
def do_get_property(self, pspec):
|
|
||||||
if pspec.name == 'fill-color':
|
|
||||||
return self._fill_color
|
|
||||||
elif pspec.name == 'stroke-color':
|
|
||||||
return self._stroke_color
|
|
||||||
elif pspec.name == 'progress-color':
|
|
||||||
return self._progress_color
|
|
||||||
elif pspec.name == 'percent':
|
|
||||||
return self._percent
|
|
||||||
|
|
||||||
def _int_to_rgb(self, int_color):
|
|
||||||
red = (int_color >> 24) & 0x000000FF
|
|
||||||
green = (int_color >> 16) & 0x000000FF
|
|
||||||
blue = (int_color >> 8) & 0x000000FF
|
|
||||||
alpha = int_color & 0x000000FF
|
|
||||||
return (red / 255.0, green / 255.0, blue / 255.0)
|
|
||||||
|
|
||||||
def do_paint_below_children(self, cr, damaged_box):
|
|
||||||
[width, height] = self.get_allocation()
|
|
||||||
|
|
||||||
line_width = 3.0
|
|
||||||
x = line_width
|
|
||||||
y = line_width
|
|
||||||
width -= line_width * 2
|
|
||||||
height -= line_width * 2
|
|
||||||
|
|
||||||
cr.move_to(x + self._radius, y);
|
|
||||||
cr.arc(x + width - self._radius, y + self._radius,
|
|
||||||
self._radius, math.pi * 1.5, math.pi * 2);
|
|
||||||
cr.arc(x + width - self._radius, x + height - self._radius,
|
|
||||||
self._radius, 0, math.pi * 0.5);
|
|
||||||
cr.arc(x + self._radius, y + height - self._radius,
|
|
||||||
self._radius, math.pi * 0.5, math.pi);
|
|
||||||
cr.arc(x + self._radius, y + self._radius, self._radius,
|
|
||||||
math.pi, math.pi * 1.5);
|
|
||||||
|
|
||||||
color = self._int_to_rgb(self._fill_color)
|
|
||||||
cr.set_source_rgb(*color)
|
|
||||||
cr.fill_preserve();
|
|
||||||
|
|
||||||
color = self._int_to_rgb(self._stroke_color)
|
|
||||||
cr.set_source_rgb(*color)
|
|
||||||
cr.set_line_width(line_width)
|
|
||||||
cr.stroke();
|
|
||||||
|
|
||||||
if self._percent > 0:
|
|
||||||
self._paint_progress_bar(cr, x, y, width, height, line_width)
|
|
||||||
|
|
||||||
def _paint_progress_bar(self, cr, x, y, width, height, line_width):
|
|
||||||
prog_x = x + line_width
|
|
||||||
prog_y = y + line_width
|
|
||||||
prog_width = (width - (line_width * 2)) * (self._percent / 100.0)
|
|
||||||
prog_height = (height - (line_width * 2))
|
|
||||||
|
|
||||||
x = prog_x
|
|
||||||
y = prog_y
|
|
||||||
width = prog_width
|
|
||||||
height = prog_height
|
|
||||||
|
|
||||||
cr.move_to(x + self._radius, y);
|
|
||||||
cr.arc(x + width - self._radius, y + self._radius,
|
|
||||||
self._radius, math.pi * 1.5, math.pi * 2);
|
|
||||||
cr.arc(x + width - self._radius, x + height - self._radius,
|
|
||||||
self._radius, 0, math.pi * 0.5);
|
|
||||||
cr.arc(x + self._radius, y + height - self._radius,
|
|
||||||
self._radius, math.pi * 0.5, math.pi);
|
|
||||||
cr.arc(x + self._radius, y + self._radius, self._radius,
|
|
||||||
math.pi, math.pi * 1.5);
|
|
||||||
|
|
||||||
color = self._int_to_rgb(self._progress_color)
|
|
||||||
cr.set_source_rgb(*color)
|
|
||||||
cr.fill_preserve();
|
|
@ -1,917 +0,0 @@
|
|||||||
# vi: ts=4 ai noet
|
|
||||||
#
|
|
||||||
# Copyright (C) 2006, 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
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
import dbus.glib
|
|
||||||
import dbus.decorators
|
|
||||||
import gobject
|
|
||||||
import gtk
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
from gettext import gettext as _
|
|
||||||
|
|
||||||
import hippo
|
|
||||||
import style
|
|
||||||
from sugar.graphics.timeline import Timeline
|
|
||||||
from wepkeydialog import WEPKeyDialog
|
|
||||||
from bubble import Bubble
|
|
||||||
|
|
||||||
import nminfo
|
|
||||||
|
|
||||||
IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
|
|
||||||
IW_AUTH_ALG_SHARED_KEY = 0x00000002
|
|
||||||
|
|
||||||
|
|
||||||
NM_DEVICE_STAGE_STRINGS=("Unknown",
|
|
||||||
"Prepare",
|
|
||||||
"Config",
|
|
||||||
"Need Users Key",
|
|
||||||
"IP Config",
|
|
||||||
"IP Config Get",
|
|
||||||
"IP Config Commit",
|
|
||||||
"Activated",
|
|
||||||
"Failed",
|
|
||||||
"Cancled"
|
|
||||||
)
|
|
||||||
|
|
||||||
NM_SERVICE = 'org.freedesktop.NetworkManager'
|
|
||||||
NM_IFACE = 'org.freedesktop.NetworkManager'
|
|
||||||
NM_IFACE_DEVICES = 'org.freedesktop.NetworkManager.Devices'
|
|
||||||
NM_PATH = '/org/freedesktop/NetworkManager'
|
|
||||||
|
|
||||||
DEVICE_TYPE_UNKNOWN = 0
|
|
||||||
DEVICE_TYPE_802_3_ETHERNET = 1
|
|
||||||
DEVICE_TYPE_802_11_WIRELESS = 2
|
|
||||||
|
|
||||||
NM_DEVICE_CAP_NONE = 0x00000000
|
|
||||||
NM_DEVICE_CAP_NM_SUPPORTED = 0x00000001
|
|
||||||
NM_DEVICE_CAP_CARRIER_DETECT = 0x00000002
|
|
||||||
NM_DEVICE_CAP_WIRELESS_SCAN = 0x00000004
|
|
||||||
|
|
||||||
|
|
||||||
sys_bus = dbus.SystemBus()
|
|
||||||
|
|
||||||
|
|
||||||
NM_802_11_CAP_NONE = 0x00000000
|
|
||||||
NM_802_11_CAP_PROTO_NONE = 0x00000001
|
|
||||||
NM_802_11_CAP_PROTO_WEP = 0x00000002
|
|
||||||
NM_802_11_CAP_PROTO_WPA = 0x00000004
|
|
||||||
NM_802_11_CAP_PROTO_WPA2 = 0x00000008
|
|
||||||
NM_802_11_CAP_KEY_MGMT_PSK = 0x00000040
|
|
||||||
NM_802_11_CAP_KEY_MGMT_802_1X = 0x00000080
|
|
||||||
NM_802_11_CAP_CIPHER_WEP40 = 0x00001000
|
|
||||||
NM_802_11_CAP_CIPHER_WEP104 = 0x00002000
|
|
||||||
NM_802_11_CAP_CIPHER_TKIP = 0x00004000
|
|
||||||
NM_802_11_CAP_CIPHER_CCMP = 0x00008000
|
|
||||||
|
|
||||||
|
|
||||||
class Network(gobject.GObject):
|
|
||||||
__gsignals__ = {
|
|
||||||
'init-failed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, op):
|
|
||||||
gobject.GObject.__init__(self)
|
|
||||||
self._op = op
|
|
||||||
self._ssid = None
|
|
||||||
self._mode = None
|
|
||||||
self._strength = 0
|
|
||||||
self._valid = False
|
|
||||||
|
|
||||||
obj = sys_bus.get_object(NM_SERVICE, self._op)
|
|
||||||
net = dbus.Interface(obj, NM_IFACE_DEVICES)
|
|
||||||
net.getProperties(reply_handler=self._update_reply_cb,
|
|
||||||
error_handler=self._update_error_cb)
|
|
||||||
|
|
||||||
def _update_reply_cb(self, *props):
|
|
||||||
self._ssid = props[1]
|
|
||||||
self._strength = props[3]
|
|
||||||
self._mode = props[6]
|
|
||||||
caps = props[7]
|
|
||||||
if caps & NM_802_11_CAP_PROTO_WPA or caps & NM_802_11_CAP_PROTO_WPA2:
|
|
||||||
# We do not support WPA at this time, so don't show
|
|
||||||
# WPA-enabled access points in the menu
|
|
||||||
logging.debug("Net(%s): ssid '%s' dropping because WPA[2] unsupported" % (self._op,
|
|
||||||
self._ssid))
|
|
||||||
self._valid = False
|
|
||||||
self.emit('init-failed')
|
|
||||||
else:
|
|
||||||
self._valid = True
|
|
||||||
logging.debug("Net(%s): ssid '%s', mode %d, strength %d" % (self._op,
|
|
||||||
self._ssid, self._mode, self._strength))
|
|
||||||
|
|
||||||
def _update_error_cb(self, err):
|
|
||||||
logging.debug("Net(%s): failed to update. (%s)" % (self._op, err))
|
|
||||||
self._valid = False
|
|
||||||
self.emit('init-failed')
|
|
||||||
|
|
||||||
def get_ssid(self):
|
|
||||||
return self._ssid
|
|
||||||
|
|
||||||
def get_op(self):
|
|
||||||
return self._op
|
|
||||||
|
|
||||||
def get_strength(self):
|
|
||||||
return self._strength
|
|
||||||
|
|
||||||
def set_strength(self, strength):
|
|
||||||
self._strength = strength
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
return self._valid
|
|
||||||
|
|
||||||
def add_to_menu(self, menu, callback, dev):
|
|
||||||
strength = self._strength
|
|
||||||
if strength > 100:
|
|
||||||
strength = 100
|
|
||||||
elif strength < 0:
|
|
||||||
strength = 0
|
|
||||||
item = NetworkMenuItem(text=self._ssid, percent=strength)
|
|
||||||
item.connect('button-press-event', callback, (dev, self))
|
|
||||||
menu.add_item(item)
|
|
||||||
|
|
||||||
|
|
||||||
class Device(gobject.GObject):
|
|
||||||
__gsignals__ = {
|
|
||||||
'init-failed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
|
|
||||||
'activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
|
|
||||||
'strength-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
|
||||||
([gobject.TYPE_PYOBJECT]))
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, op):
|
|
||||||
gobject.GObject.__init__(self)
|
|
||||||
self._op = op
|
|
||||||
self._iface = None
|
|
||||||
self._type = DEVICE_TYPE_UNKNOWN
|
|
||||||
self._udi = None
|
|
||||||
self._active = False
|
|
||||||
self._strength = 0
|
|
||||||
self._link = False
|
|
||||||
self._valid = False
|
|
||||||
self._networks = {}
|
|
||||||
self._active_net = None
|
|
||||||
self._caps = 0
|
|
||||||
|
|
||||||
obj = sys_bus.get_object(NM_SERVICE, self._op)
|
|
||||||
dev = dbus.Interface(obj, NM_IFACE_DEVICES)
|
|
||||||
dev.getProperties(reply_handler=self._update_reply_cb,
|
|
||||||
error_handler=self._update_error_cb)
|
|
||||||
|
|
||||||
def _update_reply_cb(self, *props):
|
|
||||||
self._iface = props[1]
|
|
||||||
self._type = props[2]
|
|
||||||
self._udi = props[3]
|
|
||||||
self._active = props[4]
|
|
||||||
if self._active:
|
|
||||||
self.emit('activated')
|
|
||||||
self._link = props[15]
|
|
||||||
self._caps = props[17]
|
|
||||||
|
|
||||||
if self._type == DEVICE_TYPE_802_11_WIRELESS:
|
|
||||||
old_strength = self._strength
|
|
||||||
self._strength = props[14]
|
|
||||||
if self._strength != old_strength:
|
|
||||||
self.emit('strength-changed', self._strength)
|
|
||||||
self._update_networks(props[20], props[19])
|
|
||||||
|
|
||||||
self._valid = True
|
|
||||||
|
|
||||||
def _update_networks(self, net_ops, active_op):
|
|
||||||
for op in net_ops:
|
|
||||||
net = Network(op)
|
|
||||||
self._networks[op] = net
|
|
||||||
net.connect('init-failed', self._net_init_failed)
|
|
||||||
if op == active_op:
|
|
||||||
self._active_net = op
|
|
||||||
|
|
||||||
def _update_error_cb(self, err):
|
|
||||||
logging.debug("Device(%s): failed to update. (%s)" % (self._op, err))
|
|
||||||
self._valid = False
|
|
||||||
self.emit('init-failed')
|
|
||||||
|
|
||||||
def _net_init_failed(self, net):
|
|
||||||
net_op = net.get_op()
|
|
||||||
if not self._networks.has_key(net_op):
|
|
||||||
return
|
|
||||||
if net_op == self._active_net:
|
|
||||||
self._active_net = None
|
|
||||||
del self._networks[net_op]
|
|
||||||
|
|
||||||
def _add_to_menu_wired(self, menu, callback):
|
|
||||||
item = NetworkMenuItem(_("Wired Network"), stylesheet="nm.Bubble.Wired",
|
|
||||||
hi_stylesheet="nm.Bubble.Wired.Hi",
|
|
||||||
act_stylesheet="nm.Bubble.Wired.Activated")
|
|
||||||
item.connect('button-press-event', callback, (self, None))
|
|
||||||
menu.add_item(item)
|
|
||||||
|
|
||||||
def _add_to_menu_wireless(self, menu, callback, active_only):
|
|
||||||
act_net = None
|
|
||||||
if self._active_net and self._networks.has_key(self._active_net):
|
|
||||||
act_net = self._networks[self._active_net]
|
|
||||||
|
|
||||||
# Only add the active network if active_only == True
|
|
||||||
if active_only:
|
|
||||||
if act_net:
|
|
||||||
act_net.add_to_menu(menu, callback, self)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Otherwise, add all networks _except_ the active one
|
|
||||||
for net in self._networks.values():
|
|
||||||
if not net.is_valid():
|
|
||||||
continue
|
|
||||||
if act_net == net:
|
|
||||||
continue
|
|
||||||
net.add_to_menu(menu, callback, self)
|
|
||||||
|
|
||||||
def add_to_menu(self, menu, callback, active_only=False):
|
|
||||||
if self._type == DEVICE_TYPE_802_3_ETHERNET:
|
|
||||||
self._add_to_menu_wired(menu, callback)
|
|
||||||
elif self._type == DEVICE_TYPE_802_11_WIRELESS:
|
|
||||||
self._add_to_menu_wireless(menu, callback, active_only)
|
|
||||||
|
|
||||||
def get_op(self):
|
|
||||||
return self._op
|
|
||||||
|
|
||||||
def get_network(self, op):
|
|
||||||
if self._networks.has_key(op):
|
|
||||||
return self._networks[op]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_network_ops(self):
|
|
||||||
return self._networks.keys()
|
|
||||||
|
|
||||||
def get_strength(self):
|
|
||||||
return self._strength
|
|
||||||
|
|
||||||
def set_strength(self, strength):
|
|
||||||
if strength == self._strength:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if strength >= 0 and strength <= 100:
|
|
||||||
self._strength = strength
|
|
||||||
else:
|
|
||||||
self._strength = 0
|
|
||||||
|
|
||||||
self.emit('strength-changed', self._strength)
|
|
||||||
|
|
||||||
def network_appeared(self, network):
|
|
||||||
if self._networks.has_key(network):
|
|
||||||
return
|
|
||||||
net = Network(network)
|
|
||||||
self._networks[network] = net
|
|
||||||
net.connect('init-failed', self._net_init_failed)
|
|
||||||
|
|
||||||
def network_disappeared(self, network):
|
|
||||||
if not self._networks.has_key(network):
|
|
||||||
return
|
|
||||||
if network == self._active_net:
|
|
||||||
self._active_net = None
|
|
||||||
del self._networks[network]
|
|
||||||
|
|
||||||
def get_active(self):
|
|
||||||
return self._active
|
|
||||||
|
|
||||||
def set_active(self, active, ssid=None):
|
|
||||||
self._active = active
|
|
||||||
if self._type == DEVICE_TYPE_802_11_WIRELESS:
|
|
||||||
if not ssid:
|
|
||||||
self._active_net = None
|
|
||||||
else:
|
|
||||||
for (op, net) in self._networks.items():
|
|
||||||
if net.get_ssid() == ssid:
|
|
||||||
self._active_net = op
|
|
||||||
|
|
||||||
def get_type(self):
|
|
||||||
return self._type
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
return self._valid
|
|
||||||
|
|
||||||
def set_carrier(self, on):
|
|
||||||
self._link = on
|
|
||||||
|
|
||||||
def get_capabilities(self):
|
|
||||||
return self._caps
|
|
||||||
|
|
||||||
nm_bubble_wireless = {
|
|
||||||
'fill-color' : 0x646464FF,
|
|
||||||
'stroke-color' : 0x646464FF,
|
|
||||||
'progress-color': 0x333333FF,
|
|
||||||
'spacing' : style.space_unit,
|
|
||||||
'padding' : style.space_unit * 1.5
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_bubble_wireless_hi = {
|
|
||||||
'fill-color' : 0x979797FF,
|
|
||||||
'stroke-color' : 0x979797FF,
|
|
||||||
'progress-color': 0x666666FF,
|
|
||||||
'spacing' : style.space_unit,
|
|
||||||
'padding' : style.space_unit * 1.5
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_bubble_wireless_activated = {
|
|
||||||
'fill-color' : 0xA7A7A7FF,
|
|
||||||
'stroke-color' : 0xA7A7A7FF,
|
|
||||||
'progress-color': 0x777777FF,
|
|
||||||
'spacing' : style.space_unit,
|
|
||||||
'padding' : style.space_unit * 1.5
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_bubble_wired = {
|
|
||||||
'fill-color' : 0x000000FF,
|
|
||||||
'stroke-color' : 0x000000FF,
|
|
||||||
'progress-color': 0x000000FF,
|
|
||||||
'spacing' : style.space_unit,
|
|
||||||
'padding' : style.space_unit * 1.5
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_bubble_wired_hi = {
|
|
||||||
'fill-color' : 0x333333FF,
|
|
||||||
'stroke-color' : 0x333333FF,
|
|
||||||
'progress-color': 0x000000FF,
|
|
||||||
'spacing' : style.space_unit,
|
|
||||||
'padding' : style.space_unit * 1.5
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_bubble_wired_activated = {
|
|
||||||
'fill-color' : 0x444444FF,
|
|
||||||
'stroke-color' : 0x444444FF,
|
|
||||||
'progress-color': 0x000000FF,
|
|
||||||
'spacing' : style.space_unit,
|
|
||||||
'padding' : style.space_unit * 1.5
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_menu_item_title = {
|
|
||||||
'xalign': hippo.ALIGNMENT_START,
|
|
||||||
'padding-left': 5,
|
|
||||||
'color' : 0xFFFFFFFF,
|
|
||||||
'font' : style.get_font_description('Bold', 1.2)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
style.register_stylesheet("nm.Bubble.Wireless", nm_bubble_wireless)
|
|
||||||
style.register_stylesheet("nm.Bubble.Wireless.Hi", nm_bubble_wireless_hi)
|
|
||||||
style.register_stylesheet("nm.Bubble.Wireless.Activated", nm_bubble_wireless_activated)
|
|
||||||
style.register_stylesheet("nm.Bubble.Wired", nm_bubble_wired)
|
|
||||||
style.register_stylesheet("nm.Bubble.Wired.Hi", nm_bubble_wired_hi)
|
|
||||||
style.register_stylesheet("nm.Bubble.Wired.Activated", nm_bubble_wired_activated)
|
|
||||||
style.register_stylesheet("nm.MenuItem.Title", nm_menu_item_title)
|
|
||||||
|
|
||||||
class NetworkMenuItem(Bubble):
|
|
||||||
def __init__(self, text, percent=0, stylesheet="nm.Bubble.Wireless",
|
|
||||||
hi_stylesheet="nm.Bubble.Wireless.Hi",
|
|
||||||
act_stylesheet="nm.Bubble.Wireless.Activated"):
|
|
||||||
Bubble.__init__(self, percent=percent)
|
|
||||||
self._hover = False
|
|
||||||
self._default_stylesheet = stylesheet
|
|
||||||
self._hi_stylesheet = hi_stylesheet
|
|
||||||
self._act_stylesheet = act_stylesheet
|
|
||||||
style.apply_stylesheet(self, stylesheet)
|
|
||||||
|
|
||||||
text_item = hippo.CanvasText(text=text)
|
|
||||||
style.apply_stylesheet(text_item, 'nm.MenuItem.Title')
|
|
||||||
self.append(text_item)
|
|
||||||
|
|
||||||
self.connect('motion-notify-event', self._motion_notify_event_cb)
|
|
||||||
# Disable active hilight for now...
|
|
||||||
#self.connect('button-press-event', self._button_press_event_cb)
|
|
||||||
|
|
||||||
def _motion_notify_event_cb(self, widget, event):
|
|
||||||
if event.detail == hippo.MOTION_DETAIL_ENTER:
|
|
||||||
if not self._hover:
|
|
||||||
self._hover = True
|
|
||||||
style.apply_stylesheet(self, self._hi_stylesheet)
|
|
||||||
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
|
|
||||||
if self._hover:
|
|
||||||
self._hover = False
|
|
||||||
style.apply_stylesheet(self, self._default_stylesheet)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _button_press_event_cb(self, widget, event):
|
|
||||||
style.apply_stylesheet(self, self._act_stylesheet)
|
|
||||||
return False
|
|
||||||
|
|
||||||
class NetworkMenu(gtk.Window):
|
|
||||||
__gsignals__ = {
|
|
||||||
'action': (gobject.SIGNAL_RUN_FIRST,
|
|
||||||
gobject.TYPE_NONE, ([int])),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gtk.Window.__init__(self, gtk.WINDOW_POPUP)
|
|
||||||
|
|
||||||
canvas = hippo.Canvas()
|
|
||||||
self.add(canvas)
|
|
||||||
canvas.show()
|
|
||||||
|
|
||||||
self._root = hippo.CanvasBox()
|
|
||||||
style.apply_stylesheet(self._root, 'menu')
|
|
||||||
canvas.set_root(self._root)
|
|
||||||
|
|
||||||
def add_separator(self):
|
|
||||||
separator = hippo.CanvasBox()
|
|
||||||
style.apply_stylesheet(separator, 'menu.Separator')
|
|
||||||
self._root.append(separator)
|
|
||||||
|
|
||||||
def add_item(self, item):
|
|
||||||
self._root.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NM_STATE_UNKNOWN = 0
|
|
||||||
NM_STATE_ASLEEP = 1
|
|
||||||
NM_STATE_CONNECTING = 2
|
|
||||||
NM_STATE_CONNECTED = 3
|
|
||||||
NM_STATE_DISCONNECTED = 4
|
|
||||||
|
|
||||||
ICON_WIRED = "stock-net-wired"
|
|
||||||
ICON_WIRELESS_00 = "stock-net-wireless-00"
|
|
||||||
ICON_WIRELESS_01_20 = "stock-net-wireless-01-20"
|
|
||||||
ICON_WIRELESS_21_40 = "stock-net-wireless-21-40"
|
|
||||||
ICON_WIRELESS_41_60 = "stock-net-wireless-41-60"
|
|
||||||
ICON_WIRELESS_61_80 = "stock-net-wireless-61-80"
|
|
||||||
ICON_WIRELESS_81_100 = "stock-net-wireless-81-100"
|
|
||||||
|
|
||||||
class NMClientApp:
|
|
||||||
def __init__(self):
|
|
||||||
self.nminfo = None
|
|
||||||
self._nm_present = False
|
|
||||||
self._nm_state = NM_STATE_UNKNOWN
|
|
||||||
self._update_timer = 0
|
|
||||||
self._active_device = None
|
|
||||||
self._devices = {}
|
|
||||||
self._key_dialog = None
|
|
||||||
|
|
||||||
self._icon_theme = gtk.icon_theme_get_default()
|
|
||||||
self._icons = {}
|
|
||||||
self._cur_icon = None
|
|
||||||
try:
|
|
||||||
self._icons = self._load_icons()
|
|
||||||
except RuntimeError:
|
|
||||||
logging.debug("Couldn't find required icon resources, will exit.")
|
|
||||||
os._exit(1)
|
|
||||||
self._setup_trayicon()
|
|
||||||
|
|
||||||
self._menu = None
|
|
||||||
self._hover_menu = False
|
|
||||||
self._timeline = Timeline(self)
|
|
||||||
self._timeline.add_tag('popup', 6, 6)
|
|
||||||
self._timeline.add_tag('before_popdown', 7, 7)
|
|
||||||
self._timeline.add_tag('popdown', 8, 8)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.nminfo = nminfo.NMInfo(self)
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
self._setup_dbus()
|
|
||||||
if self._nm_present:
|
|
||||||
self._get_nm_state()
|
|
||||||
self._get_initial_devices()
|
|
||||||
|
|
||||||
def _get_one_icon_pixbuf(self, name):
|
|
||||||
info = self._icon_theme.lookup_icon(name, 75, 0)
|
|
||||||
if not info or not info.get_filename():
|
|
||||||
raise RuntimeError
|
|
||||||
return gtk.gdk.pixbuf_new_from_file(info.get_filename())
|
|
||||||
|
|
||||||
def _load_icons(self):
|
|
||||||
icons = {}
|
|
||||||
icons[ICON_WIRED] = self._get_one_icon_pixbuf(ICON_WIRED)
|
|
||||||
icons[ICON_WIRELESS_00] = self._get_one_icon_pixbuf(ICON_WIRELESS_00)
|
|
||||||
icons[ICON_WIRELESS_01_20] = self._get_one_icon_pixbuf(ICON_WIRELESS_01_20)
|
|
||||||
icons[ICON_WIRELESS_21_40] = self._get_one_icon_pixbuf(ICON_WIRELESS_21_40)
|
|
||||||
icons[ICON_WIRELESS_41_60] = self._get_one_icon_pixbuf(ICON_WIRELESS_41_60)
|
|
||||||
icons[ICON_WIRELESS_61_80] = self._get_one_icon_pixbuf(ICON_WIRELESS_61_80)
|
|
||||||
icons[ICON_WIRELESS_81_100] = self._get_one_icon_pixbuf(ICON_WIRELESS_81_100)
|
|
||||||
return icons
|
|
||||||
|
|
||||||
def _get_nm_state(self):
|
|
||||||
# Grab NM's state
|
|
||||||
self._nm_obj.state(reply_handler=self._get_state_reply_cb, \
|
|
||||||
error_handler=self._get_state_error_cb)
|
|
||||||
|
|
||||||
def _get_state_reply_cb(self, state):
|
|
||||||
if self._nm_state != state:
|
|
||||||
self._schedule_icon_update(immediate=True)
|
|
||||||
self._nm_state = state
|
|
||||||
|
|
||||||
def _get_state_error_cb(self, err):
|
|
||||||
logging.debug("Failed to get NetworkManager state! %s" % err)
|
|
||||||
|
|
||||||
def _get_icon(self):
|
|
||||||
act_dev = None
|
|
||||||
if self._active_device and self._devices.has_key(self._active_device):
|
|
||||||
act_dev = self._devices[self._active_device]
|
|
||||||
|
|
||||||
pixbuf = None
|
|
||||||
if not self._nm_present \
|
|
||||||
or not act_dev \
|
|
||||||
or self._nm_state == NM_STATE_UNKNOWN \
|
|
||||||
or self._nm_state == NM_STATE_ASLEEP \
|
|
||||||
or self._nm_state == NM_STATE_DISCONNECTED:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_00]
|
|
||||||
elif act_dev.get_type() == DEVICE_TYPE_802_3_ETHERNET:
|
|
||||||
pixbuf = self._icons[ICON_WIRED]
|
|
||||||
elif act_dev.get_type() == DEVICE_TYPE_802_11_WIRELESS:
|
|
||||||
strength = act_dev.get_strength()
|
|
||||||
if strength <= 0:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_00]
|
|
||||||
elif strength >= 1 and strength <= 20:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_01_20]
|
|
||||||
elif strength >= 21 and strength <= 40:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_21_40]
|
|
||||||
elif strength >= 41 and strength <= 60:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_41_60]
|
|
||||||
elif strength >= 61 and strength <= 80:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_61_80]
|
|
||||||
elif strength >= 81 and strength:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_81_100]
|
|
||||||
|
|
||||||
if not pixbuf:
|
|
||||||
pixbuf = self._icons[ICON_WIRELESS_00]
|
|
||||||
return pixbuf
|
|
||||||
|
|
||||||
def _setup_trayicon(self):
|
|
||||||
pixbuf = self._get_icon()
|
|
||||||
self._trayicon = gtk.status_icon_new_from_pixbuf(pixbuf)
|
|
||||||
self._trayicon.connect("popup_menu", self._status_icon_clicked)
|
|
||||||
self._trayicon.connect("activate", self._status_icon_clicked)
|
|
||||||
self._schedule_icon_update()
|
|
||||||
|
|
||||||
def _status_icon_clicked(self, button=0, time=None):
|
|
||||||
self._timeline.play(None, 'popup')
|
|
||||||
|
|
||||||
def _get_menu_position(self, menu, item):
|
|
||||||
(screen, rect, orientation) = item.get_geometry()
|
|
||||||
[item_x, item_y, item_w, item_h] = rect
|
|
||||||
[menu_w, menu_h] = menu.size_request()
|
|
||||||
|
|
||||||
x = item_x + item_w - menu_w
|
|
||||||
y = item_y + item_h
|
|
||||||
|
|
||||||
x = min(x, screen.get_width() - menu_w)
|
|
||||||
x = max(0, x)
|
|
||||||
|
|
||||||
y = min(y, screen.get_height() - menu_h)
|
|
||||||
y = max(0, y)
|
|
||||||
|
|
||||||
return (x, y)
|
|
||||||
|
|
||||||
def do_popup(self, current, n_frames):
|
|
||||||
if self._menu:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._menu = self._create_menu()
|
|
||||||
self._menu.connect('enter-notify-event',
|
|
||||||
self._menu_enter_notify_event_cb)
|
|
||||||
self._menu.connect('leave-notify-event',
|
|
||||||
self._menu_leave_notify_event_cb)
|
|
||||||
(x, y) = self._get_menu_position(self._menu, self._trayicon)
|
|
||||||
self._menu.move(x, y)
|
|
||||||
self._menu.show_all()
|
|
||||||
|
|
||||||
def do_popdown(self, current, frame):
|
|
||||||
if self._menu:
|
|
||||||
self._menu.destroy()
|
|
||||||
self._menu = None
|
|
||||||
|
|
||||||
def _popdown(self):
|
|
||||||
self._timeline.play('popdown', 'popdown')
|
|
||||||
|
|
||||||
def _menu_enter_notify_event_cb(self, widget, event):
|
|
||||||
self._hover_menu = True
|
|
||||||
self._timeline.play('popup', 'popup')
|
|
||||||
|
|
||||||
def _menu_leave_notify_event_cb(self, widget, event):
|
|
||||||
self._hover_menu = False
|
|
||||||
self._popdown()
|
|
||||||
|
|
||||||
def _create_menu(self):
|
|
||||||
menu = NetworkMenu()
|
|
||||||
|
|
||||||
# Active device goes above the separator
|
|
||||||
act_dev = None
|
|
||||||
if self._active_device and self._devices.has_key(self._active_device):
|
|
||||||
act_dev = self._devices[self._active_device]
|
|
||||||
|
|
||||||
if act_dev:
|
|
||||||
act_dev.add_to_menu(menu, self._menu_item_clicked_cb, active_only=True)
|
|
||||||
menu.add_separator()
|
|
||||||
|
|
||||||
# Wired devices first, if they don't support carrier detect
|
|
||||||
for dev in self._devices.values():
|
|
||||||
if not dev.is_valid():
|
|
||||||
continue
|
|
||||||
if dev.get_type() != DEVICE_TYPE_802_3_ETHERNET:
|
|
||||||
continue
|
|
||||||
if dev.get_capabilities() & NM_DEVICE_CAP_CARRIER_DETECT:
|
|
||||||
continue
|
|
||||||
if dev == act_dev:
|
|
||||||
continue
|
|
||||||
dev.add_to_menu(menu, self._menu_item_clicked_cb)
|
|
||||||
|
|
||||||
# Wireless devices second
|
|
||||||
for dev in self._devices.values():
|
|
||||||
if not dev.is_valid():
|
|
||||||
continue
|
|
||||||
if dev.get_type() != DEVICE_TYPE_802_11_WIRELESS:
|
|
||||||
continue
|
|
||||||
dev.add_to_menu(menu, self._menu_item_clicked_cb)
|
|
||||||
|
|
||||||
return menu
|
|
||||||
|
|
||||||
def _update_icon(self):
|
|
||||||
pixbuf = self._get_icon()
|
|
||||||
if self._cur_icon != pixbuf:
|
|
||||||
self._trayicon.set_from_pixbuf(pixbuf)
|
|
||||||
self._cur_icon = pixbuf
|
|
||||||
|
|
||||||
blink = False
|
|
||||||
if self._nm_state == NM_STATE_CONNECTING:
|
|
||||||
blink = True
|
|
||||||
self._trayicon.set_blinking(blink)
|
|
||||||
|
|
||||||
self._update_timer = 0
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _schedule_icon_update(self, immediate=False):
|
|
||||||
if immediate and self._update_timer:
|
|
||||||
gobject.source_remove(self._update_timer)
|
|
||||||
self._update_timer = 0
|
|
||||||
|
|
||||||
if self._update_timer != 0:
|
|
||||||
# There is already an update scheduled
|
|
||||||
return
|
|
||||||
|
|
||||||
if immediate:
|
|
||||||
self._update_timer = gobject.idle_add(self._update_icon)
|
|
||||||
else:
|
|
||||||
self._update_timer = gobject.timeout_add(2000, self._update_icon)
|
|
||||||
|
|
||||||
def _get_initial_devices_reply_cb(self, ops):
|
|
||||||
for op in ops:
|
|
||||||
self._add_device(op)
|
|
||||||
|
|
||||||
def _dev_init_failed_cb(self, dev):
|
|
||||||
# Device failed to initialize, likely due to dbus errors or something
|
|
||||||
op = dev.get_op()
|
|
||||||
self._remove_device(op)
|
|
||||||
|
|
||||||
def _get_initial_devices_error_cb(self, err):
|
|
||||||
logging.debug("Error updating devices (%s)" % err)
|
|
||||||
|
|
||||||
def _get_initial_devices(self):
|
|
||||||
self._nm_obj.getDevices(reply_handler=self._get_initial_devices_reply_cb, \
|
|
||||||
error_handler=self._get_initial_devices_error_cb)
|
|
||||||
|
|
||||||
def _add_device(self, dev_op):
|
|
||||||
if self._devices.has_key(dev_op):
|
|
||||||
return
|
|
||||||
dev = Device(dev_op)
|
|
||||||
self._devices[dev_op] = dev
|
|
||||||
dev.connect('init-failed', self._dev_init_failed_cb)
|
|
||||||
dev.connect('activated', self._dev_activated_cb)
|
|
||||||
dev.connect('strength-changed', self._dev_strength_changed_cb)
|
|
||||||
|
|
||||||
def _remove_device(self, dev_op):
|
|
||||||
if not self._devices.has_key(dev_op):
|
|
||||||
return
|
|
||||||
if self._active_device == dev_op:
|
|
||||||
self._active_device = None
|
|
||||||
dev = self._devices[dev_op]
|
|
||||||
dev.disconnect('activated')
|
|
||||||
dev.disconnect('init-failed')
|
|
||||||
dev.disconnect('strength-changed')
|
|
||||||
del self._devices[dev_op]
|
|
||||||
self._schedule_icon_update(immediate=True)
|
|
||||||
|
|
||||||
def _dev_activated_cb(self, dev):
|
|
||||||
op = dev.get_op()
|
|
||||||
if not self._devices.has_key(op):
|
|
||||||
return
|
|
||||||
if not dev.get_active():
|
|
||||||
return
|
|
||||||
self._active_device = op
|
|
||||||
self._schedule_icon_update(immediate=True)
|
|
||||||
|
|
||||||
def _dev_strength_changed_cb(self, dev, strength):
|
|
||||||
op = dev.get_op()
|
|
||||||
if not self._devices.has_key(op):
|
|
||||||
return
|
|
||||||
if not dev.get_active():
|
|
||||||
return
|
|
||||||
self._schedule_icon_update()
|
|
||||||
|
|
||||||
def get_device(self, dev_op):
|
|
||||||
if not self._devices.has_key(dev_op):
|
|
||||||
return None
|
|
||||||
return self._devices[dev_op]
|
|
||||||
|
|
||||||
def _setup_dbus(self):
|
|
||||||
self._sig_handlers = {
|
|
||||||
'StateChange': self.state_change_sig_handler,
|
|
||||||
'DeviceAdded': self.device_added_sig_handler,
|
|
||||||
'DeviceRemoved': self.device_removed_sig_handler,
|
|
||||||
'DeviceActivationStage': self.device_activation_stage_sig_handler,
|
|
||||||
'DeviceActivating': self.device_activating_sig_handler,
|
|
||||||
'DeviceNowActive': self.device_now_active_sig_handler,
|
|
||||||
'DeviceNoLongerActive': self.device_no_longer_active_sig_handler,
|
|
||||||
'DeviceCarrierOn': self.device_carrier_on_sig_handler,
|
|
||||||
'DeviceCarrierOff': self.device_carrier_off_sig_handler,
|
|
||||||
'DeviceStrengthChanged': self.wireless_device_strength_changed_sig_handler,
|
|
||||||
'WirelessNetworkAppeared': self.wireless_network_appeared_sig_handler,
|
|
||||||
'WirelessNetworkDisappeared': self.wireless_network_disappeared_sig_handler,
|
|
||||||
'WirelessNetworkStrengthChanged': self.wireless_network_strength_changed_sig_handler
|
|
||||||
}
|
|
||||||
|
|
||||||
self._nm_proxy = sys_bus.get_object(NM_SERVICE, NM_PATH)
|
|
||||||
self._nm_obj = dbus.Interface(self._nm_proxy, NM_IFACE)
|
|
||||||
|
|
||||||
sys_bus.add_signal_receiver(self.name_owner_changed_sig_handler,
|
|
||||||
signal_name="NameOwnerChanged",
|
|
||||||
dbus_interface="org.freedesktop.DBus")
|
|
||||||
|
|
||||||
for (signal, handler) in self._sig_handlers.items():
|
|
||||||
sys_bus.add_signal_receiver(handler, signal_name=signal, dbus_interface=NM_IFACE)
|
|
||||||
|
|
||||||
# Find out whether or not NM is running
|
|
||||||
try:
|
|
||||||
bus_object = sys_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
|
||||||
name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \
|
|
||||||
dbus_interface='org.freedesktop.DBus')
|
|
||||||
if name:
|
|
||||||
self._nm_present = True
|
|
||||||
except dbus.DBusException:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _menu_item_clicked_cb(self, widget, event, dev_data):
|
|
||||||
(device, network) = dev_data
|
|
||||||
net_op = ""
|
|
||||||
if network:
|
|
||||||
net_op = network.get_op()
|
|
||||||
try:
|
|
||||||
# NM 0.6.4 and earlier have a bug which returns an
|
|
||||||
# InvalidArguments error if no security information is passed
|
|
||||||
# for wireless networks
|
|
||||||
self._nm_obj.setActiveDevice(device.get_op(), network.get_ssid())
|
|
||||||
except dbus.DBusException, e:
|
|
||||||
if str(e).find("invalid arguments"):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise dbus.DBusException(e)
|
|
||||||
|
|
||||||
self._popdown()
|
|
||||||
|
|
||||||
def get_key_for_network(self, net, async_cb, async_err_cb):
|
|
||||||
# Throw up a dialog asking for the key here, and set
|
|
||||||
# the authentication algorithm to the given one, if any
|
|
||||||
#
|
|
||||||
# Key needs to be limited to _either_ 10 or 26 digits long,
|
|
||||||
# and contain _only_ _hex_ digits, 0-9 or a-f
|
|
||||||
#
|
|
||||||
# Auth algorithm should be a dropdown of: [Open System, Shared Key],
|
|
||||||
# mapping to the values [IW_AUTH_ALG_OPEN_SYSTEM, IW_AUTH_ALG_SHARED_KEY]
|
|
||||||
# above
|
|
||||||
|
|
||||||
self._key_dialog = WEPKeyDialog(net, async_cb, async_err_cb)
|
|
||||||
self._key_dialog.connect("response", self._key_dialog_response_cb)
|
|
||||||
self._key_dialog.connect("destroy", self._key_dialog_destroy_cb)
|
|
||||||
self._key_dialog.show_all()
|
|
||||||
|
|
||||||
def _key_dialog_destroy_cb(self, widget, foo=None):
|
|
||||||
if widget != self._key_dialog:
|
|
||||||
return
|
|
||||||
self._key_dialog_response_cb(widget, gtk.RESPONSE_CANCEL)
|
|
||||||
|
|
||||||
def _key_dialog_response_cb(self, widget, response_id):
|
|
||||||
if widget != self._key_dialog:
|
|
||||||
return
|
|
||||||
key = self._key_dialog.get_key()
|
|
||||||
wep_auth_alg = self._key_dialog.get_auth_alg()
|
|
||||||
net = self._key_dialog.get_network()
|
|
||||||
(async_cb, async_err_cb) = self._key_dialog.get_callbacks()
|
|
||||||
|
|
||||||
# Clear self._key_dialog before we call destroy(), otherwise
|
|
||||||
# the destroy will trigger and we'll get called again by
|
|
||||||
# self._key_dialog_destroy_cb
|
|
||||||
self._key_dialog = None
|
|
||||||
widget.destroy()
|
|
||||||
|
|
||||||
if response_id == gtk.RESPONSE_OK:
|
|
||||||
self.nminfo.get_key_for_network_cb(
|
|
||||||
net, key, wep_auth_alg, async_cb, async_err_cb, canceled=False)
|
|
||||||
else:
|
|
||||||
self.nminfo.get_key_for_network_cb(
|
|
||||||
net, None, None, async_cb, async_err_cb, canceled=True)
|
|
||||||
|
|
||||||
def cancel_get_key_for_network(self):
|
|
||||||
# Close the wireless key dialog and just have it return
|
|
||||||
# with the 'canceled' argument set to true
|
|
||||||
if not self._key_dialog:
|
|
||||||
return
|
|
||||||
self._key_dialog_destroy_cb(self._key_dialog)
|
|
||||||
|
|
||||||
def device_activation_stage_sig_handler(self, device, stage):
|
|
||||||
logging.debug('Device Activation Stage "%s" for device %s' % (NM_DEVICE_STAGE_STRINGS[stage], device))
|
|
||||||
|
|
||||||
def state_change_sig_handler(self, state):
|
|
||||||
self._nm_state = state
|
|
||||||
self._schedule_icon_update(immediate=True)
|
|
||||||
|
|
||||||
def device_activating_sig_handler(self, device):
|
|
||||||
self._active_device = device
|
|
||||||
|
|
||||||
def device_now_active_sig_handler(self, device, ssid=None):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
self._active_device = device
|
|
||||||
self._devices[device].set_active(True, ssid)
|
|
||||||
self._schedule_icon_update(immediate=True)
|
|
||||||
|
|
||||||
def device_no_longer_active_sig_handler(self, device):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
if self._active_device == device:
|
|
||||||
self._active_device = None
|
|
||||||
self._devices[device].set_active(False)
|
|
||||||
self._schedule_icon_update(immediate=True)
|
|
||||||
|
|
||||||
def name_owner_changed_sig_handler(self, name, old, new):
|
|
||||||
if name != NM_SERVICE:
|
|
||||||
return
|
|
||||||
if (old and len(old)) and (not new and not len(new)):
|
|
||||||
# NM went away
|
|
||||||
self._nm_present = False
|
|
||||||
self._schedule_icon_update(immediate=True)
|
|
||||||
for op in self._devices.keys():
|
|
||||||
del self._devices[op]
|
|
||||||
self._devices = {}
|
|
||||||
self._active_device = None
|
|
||||||
self._nm_state = NM_STATE_UNKNOWN
|
|
||||||
elif (not old and not len(old)) and (new and len(new)):
|
|
||||||
# NM started up
|
|
||||||
self._nm_present = True
|
|
||||||
self._get_nm_state()
|
|
||||||
self._get_initial_devices()
|
|
||||||
|
|
||||||
def device_added_sig_handler(self, device):
|
|
||||||
self._add_device(device)
|
|
||||||
|
|
||||||
def device_removed_sig_handler(self, device):
|
|
||||||
self._remove_device(device)
|
|
||||||
|
|
||||||
def wireless_network_appeared_sig_handler(self, device, network):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
self._devices[device].network_appeared(network)
|
|
||||||
|
|
||||||
def wireless_network_disappeared_sig_handler(self, device, network):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
self._devices[device].network_disappeared(network)
|
|
||||||
|
|
||||||
def wireless_device_strength_changed_sig_handler(self, device, strength):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
self._devices[device].set_strength(strength)
|
|
||||||
|
|
||||||
def wireless_network_strength_changed_sig_handler(self, device, network, strength):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
net = self._devices[device].get_network(network)
|
|
||||||
if net:
|
|
||||||
net.set_strength(strength)
|
|
||||||
|
|
||||||
def device_carrier_on_sig_handler(self, device):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
self._devices[device].set_carrier(True)
|
|
||||||
|
|
||||||
def device_carrier_off_sig_handler(self, device):
|
|
||||||
if not self._devices.has_key(device):
|
|
||||||
return
|
|
||||||
self._devices[device].set_carrier(False)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
loop = gobject.MainLoop()
|
|
||||||
try:
|
|
||||||
loop.run()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
|
|
@ -1,467 +0,0 @@
|
|||||||
# vi: ts=4 ai noet
|
|
||||||
#
|
|
||||||
# Copyright (C) 2006, 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
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
import dbus.service
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import binascii
|
|
||||||
import ConfigParser
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import nmclient
|
|
||||||
try:
|
|
||||||
from sugar import env
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
NM_INFO_IFACE='org.freedesktop.NetworkManagerInfo'
|
|
||||||
NM_INFO_PATH='/org/freedesktop/NetworkManagerInfo'
|
|
||||||
|
|
||||||
|
|
||||||
class NoNetworks(dbus.DBusException):
|
|
||||||
def __init__(self):
|
|
||||||
dbus.DBusException.__init__(self)
|
|
||||||
self._dbus_error_name = NM_INFO_IFACE + '.NoNetworks'
|
|
||||||
|
|
||||||
class CanceledKeyRequestError(dbus.DBusException):
|
|
||||||
def __init__(self):
|
|
||||||
dbus.DBusException.__init__(self)
|
|
||||||
self._dbus_error_name = NM_INFO_IFACE + '.CanceledError'
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkInvalidError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NMConfig(ConfigParser.ConfigParser):
|
|
||||||
def get_bool(self, section, name):
|
|
||||||
opt = self.get(section, name)
|
|
||||||
if type(opt) == type(""):
|
|
||||||
if opt.lower() == 'yes' or opt.lower() == 'true':
|
|
||||||
return True
|
|
||||||
elif opt.lower() == 'no' or opt.lower() == 'false':
|
|
||||||
return False
|
|
||||||
raise ValueError("Invalid format for %s/%s. Should be one of [yes, no, true, false]." % (section, name))
|
|
||||||
|
|
||||||
def get_list(self, section, name):
|
|
||||||
opt = self.get(section, name)
|
|
||||||
if type(opt) == type(""):
|
|
||||||
if not len(opt):
|
|
||||||
return []
|
|
||||||
try:
|
|
||||||
return opt.split()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
raise ValueError("Invalid format for %s/%s. Should be a space-separate list." % (section, name))
|
|
||||||
|
|
||||||
def get_int(self, section, name):
|
|
||||||
opt = self.get(section, name)
|
|
||||||
try:
|
|
||||||
return int(opt)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
raise ValueError("Invalid format for %s/%s. Should be a valid integer." % (section, name))
|
|
||||||
|
|
||||||
def get_float(self, section, name):
|
|
||||||
opt = self.get(section, name)
|
|
||||||
try:
|
|
||||||
return float(opt)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
raise ValueError("Invalid format for %s/%s. Should be a valid float." % (section, name))
|
|
||||||
|
|
||||||
|
|
||||||
IW_AUTH_CIPHER_NONE = 0x00000001
|
|
||||||
IW_AUTH_CIPHER_WEP40 = 0x00000002
|
|
||||||
IW_AUTH_CIPHER_TKIP = 0x00000004
|
|
||||||
IW_AUTH_CIPHER_CCMP = 0x00000008
|
|
||||||
IW_AUTH_CIPHER_WEP104 = 0x00000010
|
|
||||||
|
|
||||||
IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
|
|
||||||
IW_AUTH_ALG_SHARED_KEY = 0x00000002
|
|
||||||
|
|
||||||
NETWORK_TYPE_UNKNOWN = 0
|
|
||||||
NETWORK_TYPE_ALLOWED = 1
|
|
||||||
NETWORK_TYPE_INVALID = 2
|
|
||||||
|
|
||||||
|
|
||||||
class Security(object):
|
|
||||||
def __init__(self, we_cipher):
|
|
||||||
self._we_cipher = we_cipher
|
|
||||||
|
|
||||||
def read_from_config(self, cfg, name):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def read_from_args(self, args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def new_from_config(cfg, name):
|
|
||||||
security = None
|
|
||||||
try:
|
|
||||||
we_cipher = cfg.get_int(name, "we_cipher")
|
|
||||||
if we_cipher == IW_AUTH_CIPHER_NONE:
|
|
||||||
security = Security(we_cipher)
|
|
||||||
elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104:
|
|
||||||
security = WEPSecurity(we_cipher)
|
|
||||||
else:
|
|
||||||
# FIXME: find a way to make WPA config option matrix not
|
|
||||||
# make you want to throw up
|
|
||||||
raise ValueError("Unsupported security combo")
|
|
||||||
security.read_from_config(cfg, name)
|
|
||||||
except (ConfigParser.NoOptionError, ValueError), e:
|
|
||||||
return None
|
|
||||||
return security
|
|
||||||
new_from_config = staticmethod(new_from_config)
|
|
||||||
|
|
||||||
def new_from_args(we_cipher, args):
|
|
||||||
security = None
|
|
||||||
try:
|
|
||||||
if we_cipher == IW_AUTH_CIPHER_NONE:
|
|
||||||
security = Security(we_cipher)
|
|
||||||
elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104:
|
|
||||||
security = WEPSecurity(we_cipher)
|
|
||||||
else:
|
|
||||||
# FIXME: find a way to make WPA config option matrix not
|
|
||||||
# make you want to throw up
|
|
||||||
raise ValueError("Unsupported security combo")
|
|
||||||
security.read_from_args(args)
|
|
||||||
except ValueError, e:
|
|
||||||
logging.debug("Error reading security information: %s" % e)
|
|
||||||
del security
|
|
||||||
return None
|
|
||||||
return security
|
|
||||||
new_from_args = staticmethod(new_from_args)
|
|
||||||
|
|
||||||
def get_properties(self):
|
|
||||||
return [dbus.Int32(self._we_cipher)]
|
|
||||||
|
|
||||||
def write_to_config(self, section, config):
|
|
||||||
config.set(section, "we_cipher", self._we_cipher)
|
|
||||||
|
|
||||||
|
|
||||||
class WEPSecurity(Security):
|
|
||||||
def read_from_args(self, args):
|
|
||||||
if len(args) != 2:
|
|
||||||
raise ValueError("not enough arguments")
|
|
||||||
key = args[0]
|
|
||||||
auth_alg = args[1]
|
|
||||||
if isinstance(key, unicode):
|
|
||||||
key = key.encode()
|
|
||||||
if not isinstance(key, str):
|
|
||||||
raise ValueError("wrong argument type for key")
|
|
||||||
if not isinstance(auth_alg, int):
|
|
||||||
raise ValueError("wrong argument type for auth_alg")
|
|
||||||
self._key = key
|
|
||||||
self._auth_alg = auth_alg
|
|
||||||
|
|
||||||
def read_from_config(self, cfg, name):
|
|
||||||
# Key should be a hex encoded string
|
|
||||||
self._key = cfg.get(name, "key")
|
|
||||||
if self._we_cipher == IW_AUTH_CIPHER_WEP40 and len(self._key) != 10:
|
|
||||||
raise ValueError("Key length not right for 40-bit WEP")
|
|
||||||
if self._we_cipher == IW_AUTH_CIPHER_WEP104 and len(self._key) != 26:
|
|
||||||
raise ValueError("Key length not right for 104-bit WEP")
|
|
||||||
|
|
||||||
try:
|
|
||||||
a = binascii.a2b_hex(self._key)
|
|
||||||
except TypeError:
|
|
||||||
raise ValueError("Key was not a hexadecimal string.")
|
|
||||||
|
|
||||||
self._auth_alg = cfg.get_int(name, "auth_alg")
|
|
||||||
if self._auth_alg != IW_AUTH_ALG_OPEN_SYSTEM and self._auth_alg != IW_AUTH_ALG_SHARED_KEY:
|
|
||||||
raise ValueError("Invalid authentication algorithm %d" % self._auth_alg)
|
|
||||||
|
|
||||||
def get_properties(self):
|
|
||||||
args = Security.get_properties(self)
|
|
||||||
args.append(dbus.String(self._key))
|
|
||||||
args.append(dbus.Int32(self._auth_alg))
|
|
||||||
return args
|
|
||||||
|
|
||||||
def write_to_config(self, section, config):
|
|
||||||
Security.write_to_config(self, section, config)
|
|
||||||
config.set(section, "key", self._key)
|
|
||||||
config.set(section, "auth_alg", self._auth_alg)
|
|
||||||
|
|
||||||
|
|
||||||
class Network:
|
|
||||||
def __init__(self, ssid):
|
|
||||||
self.ssid = ssid
|
|
||||||
self.timestamp = int(time.time())
|
|
||||||
self.bssids = []
|
|
||||||
self.we_cipher = 0
|
|
||||||
self._security = None
|
|
||||||
|
|
||||||
def get_properties(self):
|
|
||||||
bssid_list = dbus.Array([], signature="s")
|
|
||||||
for item in self.bssids:
|
|
||||||
bssid_list.append(dbus.String(item))
|
|
||||||
args = [dbus.String(self.ssid), dbus.Int32(self.timestamp), dbus.Boolean(True), bssid_list]
|
|
||||||
args += self._security.get_properties()
|
|
||||||
return tuple(args)
|
|
||||||
|
|
||||||
def get_security(self):
|
|
||||||
return self._security.get_properties()
|
|
||||||
|
|
||||||
def set_security(self, security):
|
|
||||||
self._security = security
|
|
||||||
|
|
||||||
def read_from_args(self, auto, bssid, we_cipher, args):
|
|
||||||
if auto == False:
|
|
||||||
self.timestamp = int(time.time())
|
|
||||||
if not bssid in self.bssids:
|
|
||||||
self.bssids.append(bssid)
|
|
||||||
|
|
||||||
self._security = Security.new_from_args(we_cipher, args)
|
|
||||||
if not self._security:
|
|
||||||
raise NetworkInvalidError("Invalid security information")
|
|
||||||
|
|
||||||
def read_from_config(self, config):
|
|
||||||
try:
|
|
||||||
self.timestamp = config.get_int(self.ssid, "timestamp")
|
|
||||||
except (ConfigParser.NoOptionError, ValueError), e:
|
|
||||||
raise NetworkInvalidError(e)
|
|
||||||
|
|
||||||
self._security = Security.new_from_config(config, self.ssid)
|
|
||||||
if not self._security:
|
|
||||||
raise NetworkInvalidError(e)
|
|
||||||
|
|
||||||
# The following don't need to be present
|
|
||||||
try:
|
|
||||||
self.bssids = config.get_list(self.ssid, "bssids")
|
|
||||||
except (ConfigParser.NoOptionError, ValueError), e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def write_to_config(self, config):
|
|
||||||
try:
|
|
||||||
config.add_section(self.ssid)
|
|
||||||
config.set(self.ssid, "timestamp", self.timestamp)
|
|
||||||
if len(self.bssids) > 0:
|
|
||||||
opt = " "
|
|
||||||
opt.join(self.bssids)
|
|
||||||
config.set(self.ssid, "bssids", opt)
|
|
||||||
self._security.write_to_config(self.ssid, config)
|
|
||||||
except Exception, e:
|
|
||||||
logging.debug("Error writing '%s': %s" % (self.ssid, e))
|
|
||||||
|
|
||||||
|
|
||||||
class NotFoundError(dbus.DBusException):
|
|
||||||
pass
|
|
||||||
class UnsupportedError(dbus.DBusException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class NMInfoDBusServiceHelper(dbus.service.Object):
|
|
||||||
def __init__(self, parent):
|
|
||||||
self._parent = parent
|
|
||||||
bus = dbus.SystemBus()
|
|
||||||
|
|
||||||
# If NMI is already around, don't grab the NMI service
|
|
||||||
bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
|
||||||
name = None
|
|
||||||
try:
|
|
||||||
name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \
|
|
||||||
dbus_interface='org.freedesktop.DBus')
|
|
||||||
except dbus.DBusException:
|
|
||||||
pass
|
|
||||||
if name:
|
|
||||||
logging.debug("NMI service already owned by %s, won't claim it." % name)
|
|
||||||
raise RuntimeError
|
|
||||||
|
|
||||||
bus_name = dbus.service.BusName(NM_INFO_IFACE, bus=bus)
|
|
||||||
dbus.service.Object.__init__(self, bus_name, NM_INFO_PATH)
|
|
||||||
|
|
||||||
@dbus.service.method(NM_INFO_IFACE, in_signature='i', out_signature='as')
|
|
||||||
def getNetworks(self, net_type):
|
|
||||||
ssids = self._parent.get_networks(net_type)
|
|
||||||
if len(ssids) > 0:
|
|
||||||
return dbus.Array(ssids)
|
|
||||||
|
|
||||||
raise NoNetworks()
|
|
||||||
|
|
||||||
@dbus.service.method(NM_INFO_IFACE, in_signature='si', async_callbacks=('async_cb', 'async_err_cb'))
|
|
||||||
def getNetworkProperties(self, ssid, net_type, async_cb, async_err_cb):
|
|
||||||
self._parent.get_network_properties(ssid, net_type, async_cb, async_err_cb)
|
|
||||||
|
|
||||||
@dbus.service.method(NM_INFO_IFACE)
|
|
||||||
def updateNetworkInfo(self, ssid, bauto, bssid, cipher, *args):
|
|
||||||
self._parent.update_network_info(ssid, bauto, bssid, cipher, args)
|
|
||||||
|
|
||||||
@dbus.service.method(NM_INFO_IFACE, async_callbacks=('async_cb', 'async_err_cb'))
|
|
||||||
def getKeyForNetwork(self, dev_path, net_path, ssid, attempt, new_key, async_cb, async_err_cb):
|
|
||||||
self._parent.get_key_for_network(dev_path, net_path, ssid,
|
|
||||||
attempt, new_key, async_cb, async_err_cb)
|
|
||||||
|
|
||||||
@dbus.service.method(NM_INFO_IFACE)
|
|
||||||
def cancelGetKeyForNetwork(self):
|
|
||||||
self._parent.cancel_get_key_for_network()
|
|
||||||
|
|
||||||
class NMInfo(object):
|
|
||||||
def __init__(self, client):
|
|
||||||
try:
|
|
||||||
profile_path = env.get_profile_path()
|
|
||||||
except NameError:
|
|
||||||
home = os.path.expanduser("~")
|
|
||||||
profile_path = os.path.join(home, ".sugar", "default")
|
|
||||||
self._cfg_file = os.path.join(profile_path, "nm", "networks.cfg")
|
|
||||||
self._nmclient = client
|
|
||||||
self._allowed_networks = self._read_config()
|
|
||||||
self._dbus_helper = NMInfoDBusServiceHelper(self)
|
|
||||||
|
|
||||||
def save_config(self):
|
|
||||||
self._write_config(self._allowed_networks)
|
|
||||||
|
|
||||||
def _read_config(self):
|
|
||||||
if not os.path.exists(os.path.dirname(self._cfg_file)):
|
|
||||||
os.makedirs(os.path.dirname(self._cfg_file), 0755)
|
|
||||||
if not os.path.exists(self._cfg_file):
|
|
||||||
self._write_config({})
|
|
||||||
return {}
|
|
||||||
|
|
||||||
config = NMConfig()
|
|
||||||
config.read(self._cfg_file)
|
|
||||||
networks = {}
|
|
||||||
for name in config.sections():
|
|
||||||
if not isinstance(name, unicode):
|
|
||||||
name = unicode(name)
|
|
||||||
net = Network(name)
|
|
||||||
try:
|
|
||||||
net.read_from_config(config)
|
|
||||||
networks[name] = net
|
|
||||||
except NetworkInvalidError, e:
|
|
||||||
logging.debug("Error: invalid stored network config: %s" % e)
|
|
||||||
del net
|
|
||||||
del config
|
|
||||||
return networks
|
|
||||||
|
|
||||||
def _write_config(self, networks):
|
|
||||||
fp = open(self._cfg_file, 'w')
|
|
||||||
config = NMConfig()
|
|
||||||
for net in networks.values():
|
|
||||||
net.write_to_config(config)
|
|
||||||
config.write(fp)
|
|
||||||
fp.close()
|
|
||||||
del config
|
|
||||||
|
|
||||||
def get_networks(self, net_type):
|
|
||||||
if net_type != NETWORK_TYPE_ALLOWED:
|
|
||||||
raise ValueError("Bad network type")
|
|
||||||
nets = []
|
|
||||||
for net in self._allowed_networks.values():
|
|
||||||
nets.append(net.ssid)
|
|
||||||
logging.debug("Returning networks: %s" % nets)
|
|
||||||
return nets
|
|
||||||
|
|
||||||
def get_network_properties(self, ssid, net_type, async_cb, async_err_cb):
|
|
||||||
if not isinstance(ssid, unicode):
|
|
||||||
async_err_cb(ValueError("Invalid arguments; ssid must be unicode."))
|
|
||||||
if net_type != NETWORK_TYPE_ALLOWED:
|
|
||||||
async_err_cb(ValueError("Bad network type"))
|
|
||||||
if not self._allowed_networks.has_key(ssid):
|
|
||||||
async_err_cb(NotFoundError("Network '%s' not found." % ssid))
|
|
||||||
network = self._allowed_networks[ssid]
|
|
||||||
props = network.get_properties()
|
|
||||||
|
|
||||||
# DBus workaround: the normal method return handler wraps
|
|
||||||
# the returned arguments in a tuple and then converts that to a
|
|
||||||
# struct, but NetworkManager expects a plain list of arguments.
|
|
||||||
# It turns out that the async callback method return code _doesn't_
|
|
||||||
# wrap the returned arguments in a tuple, so as a workaround use
|
|
||||||
# the async callback stuff here even though we're not doing it
|
|
||||||
# asynchronously.
|
|
||||||
async_cb(*props)
|
|
||||||
|
|
||||||
def update_network_info(self, ssid, auto, bssid, we_cipher, args):
|
|
||||||
if not isinstance(ssid, unicode):
|
|
||||||
raise ValueError("Invalid arguments; ssid must be unicode.")
|
|
||||||
if self._allowed_networks.has_key(ssid):
|
|
||||||
del self._allowed_networks[ssid]
|
|
||||||
net = Network(ssid)
|
|
||||||
try:
|
|
||||||
net.read_from_args(auto, bssid, we_cipher, args)
|
|
||||||
logging.debug("Updated network information for '%s'." % ssid)
|
|
||||||
self._allowed_networks[ssid] = net
|
|
||||||
self.save_config()
|
|
||||||
except NetworkInvalidError, e:
|
|
||||||
logging.debug("Error updating network information: %s" % e)
|
|
||||||
del net
|
|
||||||
|
|
||||||
def get_key_for_network(self, dev_op, net_op, ssid, attempt, new_key, async_cb, async_err_cb):
|
|
||||||
if not isinstance(ssid, unicode):
|
|
||||||
raise ValueError("Invalid arguments; ssid must be unicode.")
|
|
||||||
if self._allowed_networks.has_key(ssid) and not new_key:
|
|
||||||
# We've got the info already
|
|
||||||
net = self._allowed_networks[ssid]
|
|
||||||
async_cb(tuple(net.get_security()))
|
|
||||||
return
|
|
||||||
|
|
||||||
# Otherwise, ask the user for it
|
|
||||||
net = None
|
|
||||||
dev = self._nmclient.get_device(dev_op)
|
|
||||||
if not dev:
|
|
||||||
async_err_cb(NotFoundError("Device was unknown."))
|
|
||||||
return
|
|
||||||
|
|
||||||
if dev.get_type() == nmclient.DEVICE_TYPE_802_3_ETHERNET:
|
|
||||||
# We don't support wired 802.1x yet...
|
|
||||||
async_err_cb(UnsupportedError("Device type is unsupported by NMI."))
|
|
||||||
return
|
|
||||||
|
|
||||||
net = dev.get_network(net_op)
|
|
||||||
if not net:
|
|
||||||
async_err_cb(NotFoundError("Network was unknown."))
|
|
||||||
return
|
|
||||||
|
|
||||||
self._nmclient.get_key_for_network(net, async_cb, async_err_cb)
|
|
||||||
|
|
||||||
def get_key_for_network_cb(self, net, key, auth_alg, async_cb, async_err_cb, canceled=False):
|
|
||||||
"""
|
|
||||||
Called by the NMClient when the Wireless Network Key dialog
|
|
||||||
is closed.
|
|
||||||
"""
|
|
||||||
if canceled:
|
|
||||||
e = CanceledKeyRequestError("Request was canceled.")
|
|
||||||
# key dialog dialog was canceled; send the error back to NM
|
|
||||||
async_err_cb(e)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not key or not auth_alg:
|
|
||||||
# no key returned, *** BUG ***; the key dialog
|
|
||||||
# should always return either a key + auth_alg, or a
|
|
||||||
#cancel error
|
|
||||||
raise RuntimeError("No key or auth alg given! Bug!")
|
|
||||||
|
|
||||||
we_cipher = None
|
|
||||||
if len(key) == 26:
|
|
||||||
we_cipher = IW_AUTH_CIPHER_WEP104
|
|
||||||
elif len(key) == 10:
|
|
||||||
we_cipher = IW_AUTH_CIPHER_WEP40
|
|
||||||
else:
|
|
||||||
raise RuntimeError("Invalid key length!")
|
|
||||||
|
|
||||||
# Stuff the returned key and auth algorithm into a security object
|
|
||||||
# and return it to NetworkManager
|
|
||||||
sec = Security.new_from_args(we_cipher, (key, auth_alg))
|
|
||||||
if not sec:
|
|
||||||
raise RuntimeError("Invalid security arguments.")
|
|
||||||
props = sec.get_properties()
|
|
||||||
a = tuple(props)
|
|
||||||
async_cb(*a)
|
|
||||||
|
|
||||||
def cancel_get_key_for_network(self):
|
|
||||||
# Tell the NMClient to close the key request dialog
|
|
||||||
self._nmclient.cancel_get_key_for_network()
|
|
@ -1,59 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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 library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
|
|
||||||
### Deprecated: we should drop this once we removed stylesheets ###
|
|
||||||
|
|
||||||
_styles = {}
|
|
||||||
|
|
||||||
screen_factor = gtk.gdk.screen_width() / 1200.0
|
|
||||||
|
|
||||||
space_unit = 9 * screen_factor
|
|
||||||
separator_thickness = 3 * screen_factor
|
|
||||||
|
|
||||||
standard_icon_scale = 1.0 * screen_factor
|
|
||||||
small_icon_scale = 0.5 * screen_factor
|
|
||||||
medium_icon_scale = 1.5 * screen_factor
|
|
||||||
large_icon_scale = 2.0 * screen_factor
|
|
||||||
xlarge_icon_scale = 3.0 * screen_factor
|
|
||||||
|
|
||||||
default_font_size = 9.0 * screen_factor
|
|
||||||
|
|
||||||
def load_stylesheet(module):
|
|
||||||
for objname in dir(module):
|
|
||||||
if not objname.startswith('_'):
|
|
||||||
obj = getattr(module, objname)
|
|
||||||
if isinstance(obj, dict):
|
|
||||||
register_stylesheet(objname.replace('_', '.'), obj)
|
|
||||||
|
|
||||||
def register_stylesheet(name, style):
|
|
||||||
_styles[name] = style
|
|
||||||
|
|
||||||
def apply_stylesheet(item, stylesheet_name):
|
|
||||||
if _styles.has_key(stylesheet_name):
|
|
||||||
style_sheet = _styles[stylesheet_name]
|
|
||||||
for name in style_sheet.keys():
|
|
||||||
item.set_property(name, style_sheet[name])
|
|
||||||
else:
|
|
||||||
logging.debug('Stylesheet %s not found.' % stylesheet_name)
|
|
||||||
|
|
||||||
def get_font_description(style, relative_size):
|
|
||||||
base_size = 18 * screen_factor
|
|
||||||
return '%s %dpx' % (style, int(base_size * relative_size))
|
|
@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# vi: ts=4 ai noet
|
|
||||||
#
|
|
||||||
# Copyright (C) 2006, 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
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import pygtk
|
|
||||||
pygtk.require('2.0')
|
|
||||||
|
|
||||||
from sugar import logger
|
|
||||||
from sugar import env
|
|
||||||
|
|
||||||
sys.path.insert(0, env.get_services_dir())
|
|
||||||
|
|
||||||
from nm import nmclient
|
|
||||||
|
|
||||||
logger.start('nm-applet')
|
|
||||||
|
|
||||||
logging.info('Starting network applet')
|
|
||||||
|
|
||||||
app = nmclient.NMClientApp()
|
|
||||||
app.run()
|
|
@ -1,81 +0,0 @@
|
|||||||
# vi: ts=4 ai noet
|
|
||||||
#
|
|
||||||
# Copyright (C) 2006, 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
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
|
|
||||||
IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
|
|
||||||
IW_AUTH_ALG_SHARED_KEY = 0x00000002
|
|
||||||
|
|
||||||
class WEPKeyDialog(gtk.Dialog):
|
|
||||||
def __init__(self, net, async_cb, async_err_cb):
|
|
||||||
gtk.Dialog.__init__(self)
|
|
||||||
self.set_title("Wireless Key Required")
|
|
||||||
|
|
||||||
self._net = net
|
|
||||||
self._async_cb = async_cb
|
|
||||||
self._async_err_cb = async_err_cb
|
|
||||||
|
|
||||||
self.set_has_separator(False)
|
|
||||||
|
|
||||||
label = gtk.Label("A wireless encryption key is required for\n" \
|
|
||||||
" the wireless network '%s'." % net.get_ssid())
|
|
||||||
self.vbox.pack_start(label)
|
|
||||||
|
|
||||||
self._entry = gtk.Entry()
|
|
||||||
self._entry.props.visibility = False
|
|
||||||
self._entry.connect('changed', self._entry_changed_cb)
|
|
||||||
self.vbox.pack_start(self._entry)
|
|
||||||
self.vbox.show_all()
|
|
||||||
|
|
||||||
self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
|
||||||
gtk.STOCK_OK, gtk.RESPONSE_OK)
|
|
||||||
|
|
||||||
self.set_default_response(gtk.RESPONSE_OK)
|
|
||||||
self._update_response_sensitivity()
|
|
||||||
|
|
||||||
def get_key(self):
|
|
||||||
return self._entry.get_text()
|
|
||||||
|
|
||||||
def get_auth_alg(self):
|
|
||||||
return IW_AUTH_ALG_OPEN_SYSTEM
|
|
||||||
|
|
||||||
def get_network(self):
|
|
||||||
return self._net
|
|
||||||
|
|
||||||
def get_callbacks(self):
|
|
||||||
return (self._async_cb, self._async_err_cb)
|
|
||||||
|
|
||||||
def _entry_changed_cb(self, entry):
|
|
||||||
self._update_response_sensitivity()
|
|
||||||
|
|
||||||
def _update_response_sensitivity(self):
|
|
||||||
key = self.get_key()
|
|
||||||
|
|
||||||
is_hex = True
|
|
||||||
for c in key:
|
|
||||||
if not 'a' <= c <= 'f' and not '0' <= c <= '9':
|
|
||||||
is_hex = False
|
|
||||||
|
|
||||||
valid_len = (len(key) == 10 or len(key) == 26)
|
|
||||||
self.set_response_sensitive(gtk.RESPONSE_OK, is_hex and valid_len)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
dialog = WEPKeyDialog()
|
|
||||||
dialog.run()
|
|
||||||
|
|
||||||
print dialog.get_key()
|
|
@ -62,12 +62,6 @@ model = ShellModel()
|
|||||||
service = ShellService(model)
|
service = ShellService(model)
|
||||||
shell = Shell(model)
|
shell = Shell(model)
|
||||||
|
|
||||||
# Start the NetworkManager applet
|
|
||||||
# FIXME: do this somewhere else, better planned out
|
|
||||||
args = ["sugar-nm-applet"]
|
|
||||||
flags = gobject.SPAWN_SEARCH_PATH
|
|
||||||
result = gobject.spawn_async(args, flags=flags, standard_output=False)
|
|
||||||
|
|
||||||
tbh = TracebackUtils.TracebackHelper()
|
tbh = TracebackUtils.TracebackHelper()
|
||||||
try:
|
try:
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
@ -8,7 +8,6 @@ sugar_PYTHON = \
|
|||||||
eventframe.py \
|
eventframe.py \
|
||||||
frame.py \
|
frame.py \
|
||||||
ZoomBox.py \
|
ZoomBox.py \
|
||||||
notificationtray.py \
|
|
||||||
overlaybox.py \
|
overlaybox.py \
|
||||||
PanelWindow.py \
|
PanelWindow.py \
|
||||||
framepopupcontext.py
|
framepopupcontext.py
|
||||||
|
@ -26,7 +26,6 @@ from view.frame.overlaybox import OverlayBox
|
|||||||
from view.frame.FriendsBox import FriendsBox
|
from view.frame.FriendsBox import FriendsBox
|
||||||
from view.frame.PanelWindow import PanelWindow
|
from view.frame.PanelWindow import PanelWindow
|
||||||
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
|
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
|
||||||
from view.frame.notificationtray import NotificationTray
|
|
||||||
from view.frame.framepopupcontext import FramePopupContext
|
from view.frame.framepopupcontext import FramePopupContext
|
||||||
from model.ShellModel import ShellModel
|
from model.ShellModel import ShellModel
|
||||||
from sugar.graphics.timeline import Timeline
|
from sugar.graphics.timeline import Timeline
|
||||||
@ -87,16 +86,6 @@ class Frame:
|
|||||||
box = ZoomBox(self._shell, self._popup_context)
|
box = ZoomBox(self._shell, self._popup_context)
|
||||||
root.append(box)
|
root.append(box)
|
||||||
|
|
||||||
tray = NotificationTray()
|
|
||||||
tray_box = hippo.CanvasBox(box_width=units.grid_to_pixels(1),
|
|
||||||
box_height=units.grid_to_pixels(1),
|
|
||||||
xalign=hippo.ALIGNMENT_END)
|
|
||||||
|
|
||||||
tray_widget = hippo.CanvasWidget()
|
|
||||||
tray_widget.props.widget = tray
|
|
||||||
tray_box.append(tray_widget, gtk.EXPAND)
|
|
||||||
root.append(tray_box)
|
|
||||||
|
|
||||||
box = OverlayBox(self._shell)
|
box = OverlayBox(self._shell)
|
||||||
root.append(box, hippo.PACK_FIXED)
|
root.append(box, hippo.PACK_FIXED)
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import gtk
|
|
||||||
|
|
||||||
from _sugar import TrayManager
|
|
||||||
|
|
||||||
class NotificationTray(gtk.HBox):
|
|
||||||
def __init__(self):
|
|
||||||
gtk.HBox.__init__(self)
|
|
||||||
|
|
||||||
self._manager = TrayManager()
|
|
||||||
self._manager.connect('tray-icon-added', self._icon_added_cb)
|
|
||||||
self._manager.connect('tray-icon-removed', self._icon_removed_cb)
|
|
||||||
self._manager.manage_screen(gtk.gdk.screen_get_default())
|
|
||||||
|
|
||||||
def _icon_added_cb(self, manager, icon):
|
|
||||||
self.pack_start(icon, False)
|
|
||||||
|
|
||||||
def _icon_removed_cb(self, manager, icon):
|
|
||||||
icon.destroy()
|
|
Loading…
Reference in New Issue
Block a user