Merge branch 'master' of git+ssh://j5@dev.laptop.org/git/sugar
This commit is contained in:
commit
655d7f4174
7
NEWS
7
NEWS
@ -1,3 +1,10 @@
|
||||
* #2012: Fix palette position on the left frame panel. (marco)
|
||||
* #2297: Make activity name translatable. (danw)
|
||||
* #2695: Recognize text files as such. (tomeu)
|
||||
* #2669: Add a border to the inner of the frame. (marco)
|
||||
* #2703: Update macedonian translation. (ArangelAngov)
|
||||
* #2543: Offer multiple activities for opening clipboard objects. (tomeu)
|
||||
|
||||
Snapshot d93122bf5e
|
||||
|
||||
* #2751 Add keybindings for max/min brightness/volume
|
||||
|
@ -10,6 +10,8 @@ libsugarui_la_SOURCES = \
|
||||
$(BUILT_SOURCES) \
|
||||
eggaccelerators.c \
|
||||
eggaccelerators.h \
|
||||
sexy-icon-entry.h \
|
||||
sexy-icon-entry.c \
|
||||
sugar-address-entry.c \
|
||||
sugar-address-entry.h \
|
||||
sugar-key-grabber.c \
|
||||
|
984
lib/ui/sexy-icon-entry.c
Normal file
984
lib/ui/sexy-icon-entry.c
Normal file
@ -0,0 +1,984 @@
|
||||
/*
|
||||
* @file libsexy/sexy-icon-entry.c Entry widget
|
||||
*
|
||||
* @Copyright (C) 2004-2006 Christian Hammond.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <sexy-icon-entry.h>
|
||||
#include <string.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define ICON_MARGIN 2
|
||||
#define MAX_ICONS 2
|
||||
|
||||
#define IS_VALID_ICON_ENTRY_POSITION(pos) \
|
||||
((pos) == SEXY_ICON_ENTRY_PRIMARY || \
|
||||
(pos) == SEXY_ICON_ENTRY_SECONDARY)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkImage *icon;
|
||||
gboolean highlight;
|
||||
gboolean hovered;
|
||||
GdkWindow *window;
|
||||
|
||||
} SexyIconInfo;
|
||||
|
||||
struct _SexyIconEntryPriv
|
||||
{
|
||||
SexyIconInfo icons[MAX_ICONS];
|
||||
|
||||
gulong icon_released_id;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ICON_PRESSED,
|
||||
ICON_RELEASED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static void sexy_icon_entry_class_init(SexyIconEntryClass *klass);
|
||||
static void sexy_icon_entry_editable_init(GtkEditableClass *iface);
|
||||
static void sexy_icon_entry_init(SexyIconEntry *entry);
|
||||
static void sexy_icon_entry_finalize(GObject *obj);
|
||||
static void sexy_icon_entry_destroy(GtkObject *obj);
|
||||
static void sexy_icon_entry_map(GtkWidget *widget);
|
||||
static void sexy_icon_entry_unmap(GtkWidget *widget);
|
||||
static void sexy_icon_entry_realize(GtkWidget *widget);
|
||||
static void sexy_icon_entry_unrealize(GtkWidget *widget);
|
||||
static void sexy_icon_entry_size_request(GtkWidget *widget,
|
||||
GtkRequisition *requisition);
|
||||
static void sexy_icon_entry_size_allocate(GtkWidget *widget,
|
||||
GtkAllocation *allocation);
|
||||
static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event);
|
||||
static gint sexy_icon_entry_enter_notify(GtkWidget *widget,
|
||||
GdkEventCrossing *event);
|
||||
static gint sexy_icon_entry_leave_notify(GtkWidget *widget,
|
||||
GdkEventCrossing *event);
|
||||
static gint sexy_icon_entry_button_press(GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
static gint sexy_icon_entry_button_release(GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
|
||||
static GtkEntryClass *parent_class = NULL;
|
||||
static guint signals[LAST_SIGNAL] = {0};
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
|
||||
0,
|
||||
G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
|
||||
sexy_icon_entry_editable_init));
|
||||
|
||||
static void
|
||||
sexy_icon_entry_class_init(SexyIconEntryClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
GtkEntryClass *entry_class;
|
||||
|
||||
parent_class = g_type_class_peek_parent(klass);
|
||||
|
||||
gobject_class = G_OBJECT_CLASS(klass);
|
||||
object_class = GTK_OBJECT_CLASS(klass);
|
||||
widget_class = GTK_WIDGET_CLASS(klass);
|
||||
entry_class = GTK_ENTRY_CLASS(klass);
|
||||
|
||||
gobject_class->finalize = sexy_icon_entry_finalize;
|
||||
|
||||
object_class->destroy = sexy_icon_entry_destroy;
|
||||
|
||||
widget_class->map = sexy_icon_entry_map;
|
||||
widget_class->unmap = sexy_icon_entry_unmap;
|
||||
widget_class->realize = sexy_icon_entry_realize;
|
||||
widget_class->unrealize = sexy_icon_entry_unrealize;
|
||||
widget_class->size_request = sexy_icon_entry_size_request;
|
||||
widget_class->size_allocate = sexy_icon_entry_size_allocate;
|
||||
widget_class->expose_event = sexy_icon_entry_expose;
|
||||
widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
|
||||
widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
|
||||
widget_class->button_press_event = sexy_icon_entry_button_press;
|
||||
widget_class->button_release_event = sexy_icon_entry_button_release;
|
||||
|
||||
/**
|
||||
* SexyIconEntry::icon-pressed:
|
||||
* @entry: The entry on which the signal is emitted.
|
||||
* @icon_pos: The position of the clicked icon.
|
||||
* @button: The mouse button clicked.
|
||||
*
|
||||
* The ::icon-pressed signal is emitted when an icon is clicked.
|
||||
*/
|
||||
signals[ICON_PRESSED] =
|
||||
g_signal_new("icon_pressed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed),
|
||||
NULL, NULL,
|
||||
gtk_marshal_VOID__INT_INT,
|
||||
G_TYPE_NONE, 2,
|
||||
G_TYPE_INT,
|
||||
G_TYPE_INT);
|
||||
|
||||
/**
|
||||
* SexyIconEntry::icon-released:
|
||||
* @entry: The entry on which the signal is emitted.
|
||||
* @icon_pos: The position of the clicked icon.
|
||||
* @button: The mouse button clicked.
|
||||
*
|
||||
* The ::icon-released signal is emitted on the button release from a
|
||||
* mouse click.
|
||||
*/
|
||||
signals[ICON_RELEASED] =
|
||||
g_signal_new("icon_released",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET(SexyIconEntryClass, icon_released),
|
||||
NULL, NULL,
|
||||
gtk_marshal_VOID__INT_INT,
|
||||
G_TYPE_NONE, 2,
|
||||
G_TYPE_INT,
|
||||
G_TYPE_INT);
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_editable_init(GtkEditableClass *iface)
|
||||
{
|
||||
};
|
||||
|
||||
static void
|
||||
sexy_icon_entry_init(SexyIconEntry *entry)
|
||||
{
|
||||
entry->priv = g_new0(SexyIconEntryPriv, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_finalize(GObject *obj)
|
||||
{
|
||||
SexyIconEntry *entry;
|
||||
|
||||
g_return_if_fail(obj != NULL);
|
||||
g_return_if_fail(SEXY_IS_ICON_ENTRY(obj));
|
||||
|
||||
entry = SEXY_ICON_ENTRY(obj);
|
||||
|
||||
g_free(entry->priv);
|
||||
|
||||
if (G_OBJECT_CLASS(parent_class)->finalize)
|
||||
G_OBJECT_CLASS(parent_class)->finalize(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_destroy(GtkObject *obj)
|
||||
{
|
||||
SexyIconEntry *entry;
|
||||
|
||||
entry = SEXY_ICON_ENTRY(obj);
|
||||
|
||||
sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_PRIMARY, NULL);
|
||||
sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_SECONDARY, NULL);
|
||||
|
||||
if (GTK_OBJECT_CLASS(parent_class)->destroy)
|
||||
GTK_OBJECT_CLASS(parent_class)->destroy(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_map(GtkWidget *widget)
|
||||
{
|
||||
if (GTK_WIDGET_REALIZED(widget) && !GTK_WIDGET_MAPPED(widget))
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
int i;
|
||||
|
||||
GTK_WIDGET_CLASS(parent_class)->map(widget);
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
if (entry->priv->icons[i].icon != NULL)
|
||||
gdk_window_show(entry->priv->icons[i].window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_unmap(GtkWidget *widget)
|
||||
{
|
||||
if (GTK_WIDGET_MAPPED(widget))
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
if (entry->priv->icons[i].icon != NULL)
|
||||
gdk_window_hide(entry->priv->icons[i].window);
|
||||
}
|
||||
|
||||
GTK_WIDGET_CLASS(parent_class)->unmap(widget);
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
|
||||
{
|
||||
GtkRequisition requisition;
|
||||
gint menu_icon_width;
|
||||
gint width;
|
||||
SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
|
||||
|
||||
if (icon_info->icon == NULL)
|
||||
return 0;
|
||||
|
||||
gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition);
|
||||
gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL);
|
||||
|
||||
width = MAX(requisition.width, menu_icon_width);
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
static void
|
||||
get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET(entry);
|
||||
gint focus_width;
|
||||
gboolean interior_focus;
|
||||
|
||||
gtk_widget_style_get(widget,
|
||||
"interior-focus", &interior_focus,
|
||||
"focus-line-width", &focus_width,
|
||||
NULL);
|
||||
|
||||
if (gtk_entry_get_has_frame(GTK_ENTRY(entry)))
|
||||
{
|
||||
*xborder = widget->style->xthickness;
|
||||
*yborder = widget->style->ythickness;
|
||||
}
|
||||
else
|
||||
{
|
||||
*xborder = 0;
|
||||
*yborder = 0;
|
||||
}
|
||||
|
||||
if (!interior_focus)
|
||||
{
|
||||
*xborder += focus_width;
|
||||
*yborder += focus_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET(entry);
|
||||
GtkRequisition requisition;
|
||||
gint xborder, yborder;
|
||||
|
||||
gtk_widget_get_child_requisition(widget, &requisition);
|
||||
get_borders(entry, &xborder, &yborder);
|
||||
|
||||
alloc->x = xborder;
|
||||
alloc->y = yborder;
|
||||
alloc->width = widget->allocation.width - xborder * 2;
|
||||
alloc->height = requisition.height - yborder * 2;
|
||||
}
|
||||
|
||||
static void
|
||||
get_icon_allocation(SexyIconEntry *icon_entry,
|
||||
gboolean left,
|
||||
GtkAllocation *widget_alloc,
|
||||
GtkAllocation *text_area_alloc,
|
||||
GtkAllocation *allocation,
|
||||
SexyIconEntryPosition *icon_pos)
|
||||
{
|
||||
gboolean rtl;
|
||||
|
||||
rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) ==
|
||||
GTK_TEXT_DIR_RTL);
|
||||
|
||||
if (left)
|
||||
*icon_pos = (rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY);
|
||||
else
|
||||
*icon_pos = (rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY);
|
||||
|
||||
allocation->y = text_area_alloc->y;
|
||||
allocation->width = get_icon_width(icon_entry, *icon_pos);
|
||||
allocation->height = text_area_alloc->height;
|
||||
|
||||
if (left)
|
||||
allocation->x = text_area_alloc->x + ICON_MARGIN;
|
||||
else
|
||||
{
|
||||
allocation->x = text_area_alloc->x + text_area_alloc->width -
|
||||
allocation->width - ICON_MARGIN;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_realize(GtkWidget *widget)
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
GdkWindowAttr attributes;
|
||||
gint attributes_mask;
|
||||
int i;
|
||||
|
||||
GTK_WIDGET_CLASS(parent_class)->realize(widget);
|
||||
|
||||
attributes.x = 0;
|
||||
attributes.y = 0;
|
||||
attributes.width = 1;
|
||||
attributes.height = 1;
|
||||
attributes.window_type = GDK_WINDOW_CHILD;
|
||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
||||
attributes.visual = gtk_widget_get_visual(widget);
|
||||
attributes.colormap = gtk_widget_get_colormap(widget);
|
||||
attributes.event_mask = gtk_widget_get_events(widget);
|
||||
attributes.event_mask |=
|
||||
(GDK_EXPOSURE_MASK
|
||||
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
|
||||
| GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
|
||||
|
||||
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
SexyIconInfo *icon_info;
|
||||
|
||||
icon_info = &entry->priv->icons[i];
|
||||
icon_info->window = gdk_window_new(widget->window, &attributes,
|
||||
attributes_mask);
|
||||
gdk_window_set_user_data(icon_info->window, widget);
|
||||
|
||||
gdk_window_set_background(icon_info->window,
|
||||
&widget->style->base[GTK_WIDGET_STATE(widget)]);
|
||||
}
|
||||
|
||||
gtk_widget_queue_resize(widget);
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_unrealize(GtkWidget *widget)
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
int i;
|
||||
|
||||
GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
SexyIconInfo *icon_info = &entry->priv->icons[i];
|
||||
|
||||
gdk_window_destroy(icon_info->window);
|
||||
icon_info->window = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
GtkEntry *gtkentry;
|
||||
SexyIconEntry *entry;
|
||||
gint icon_widths = 0;
|
||||
int i;
|
||||
|
||||
gtkentry = GTK_ENTRY(widget);
|
||||
entry = SEXY_ICON_ENTRY(widget);
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
int icon_width = get_icon_width(entry, i);
|
||||
|
||||
if (icon_width > 0)
|
||||
icon_widths += icon_width + ICON_MARGIN;
|
||||
}
|
||||
|
||||
GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
|
||||
|
||||
if (icon_widths > requisition->width)
|
||||
requisition->width += icon_widths;
|
||||
}
|
||||
|
||||
static void
|
||||
place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
|
||||
{
|
||||
SexyIconEntryPosition left_icon_pos;
|
||||
SexyIconEntryPosition right_icon_pos;
|
||||
GtkAllocation left_icon_alloc;
|
||||
GtkAllocation right_icon_alloc;
|
||||
GtkAllocation text_area_alloc;
|
||||
|
||||
get_text_area_size(icon_entry, &text_area_alloc);
|
||||
get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc,
|
||||
&left_icon_alloc, &left_icon_pos);
|
||||
get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc,
|
||||
&right_icon_alloc, &right_icon_pos);
|
||||
|
||||
if (left_icon_alloc.width > 0)
|
||||
{
|
||||
text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
|
||||
ICON_MARGIN;
|
||||
}
|
||||
|
||||
if (right_icon_alloc.width > 0)
|
||||
text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
|
||||
|
||||
text_area_alloc.width -= text_area_alloc.x;
|
||||
|
||||
gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window,
|
||||
left_icon_alloc.x, left_icon_alloc.y,
|
||||
left_icon_alloc.width, left_icon_alloc.height);
|
||||
|
||||
gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window,
|
||||
right_icon_alloc.x, right_icon_alloc.y,
|
||||
right_icon_alloc.width, right_icon_alloc.height);
|
||||
|
||||
gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area,
|
||||
text_area_alloc.x, text_area_alloc.y,
|
||||
text_area_alloc.width, text_area_alloc.height);
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
|
||||
{
|
||||
g_return_if_fail(SEXY_IS_ICON_ENTRY(widget));
|
||||
g_return_if_fail(allocation != NULL);
|
||||
|
||||
widget->allocation = *allocation;
|
||||
|
||||
GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
|
||||
|
||||
if (GTK_WIDGET_REALIZED(widget))
|
||||
place_windows(SEXY_ICON_ENTRY(widget), allocation);
|
||||
}
|
||||
|
||||
static GdkPixbuf *
|
||||
get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
|
||||
{
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
gchar *stock_id;
|
||||
SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
|
||||
GtkIconSize size;
|
||||
|
||||
switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon)))
|
||||
{
|
||||
case GTK_IMAGE_PIXBUF:
|
||||
pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon));
|
||||
g_object_ref(pixbuf);
|
||||
break;
|
||||
|
||||
case GTK_IMAGE_STOCK:
|
||||
gtk_image_get_stock(GTK_IMAGE(icon_info->icon), &stock_id, &size);
|
||||
pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry),
|
||||
stock_id, size, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
/* Kudos to the gnome-panel guys. */
|
||||
static void
|
||||
colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
|
||||
{
|
||||
gint i, j;
|
||||
gint width, height, has_alpha, src_rowstride, dest_rowstride;
|
||||
guchar *target_pixels;
|
||||
guchar *original_pixels;
|
||||
guchar *pix_src;
|
||||
guchar *pix_dest;
|
||||
int val;
|
||||
guchar r, g, b;
|
||||
|
||||
has_alpha = gdk_pixbuf_get_has_alpha(src);
|
||||
width = gdk_pixbuf_get_width(src);
|
||||
height = gdk_pixbuf_get_height(src);
|
||||
src_rowstride = gdk_pixbuf_get_rowstride(src);
|
||||
dest_rowstride = gdk_pixbuf_get_rowstride(dest);
|
||||
original_pixels = gdk_pixbuf_get_pixels(src);
|
||||
target_pixels = gdk_pixbuf_get_pixels(dest);
|
||||
|
||||
for (i = 0; i < height; i++)
|
||||
{
|
||||
pix_dest = target_pixels + i * dest_rowstride;
|
||||
pix_src = original_pixels + i * src_rowstride;
|
||||
|
||||
for (j = 0; j < width; j++)
|
||||
{
|
||||
r = *(pix_src++);
|
||||
g = *(pix_src++);
|
||||
b = *(pix_src++);
|
||||
|
||||
val = r + shift;
|
||||
*(pix_dest++) = CLAMP(val, 0, 255);
|
||||
|
||||
val = g + shift;
|
||||
*(pix_dest++) = CLAMP(val, 0, 255);
|
||||
|
||||
val = b + shift;
|
||||
*(pix_dest++) = CLAMP(val, 0, 255);
|
||||
|
||||
if (has_alpha)
|
||||
*(pix_dest++) = *(pix_src++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
|
||||
GdkPixbuf *pixbuf;
|
||||
gint x, y, width, height;
|
||||
|
||||
if (icon_info->icon == NULL || !GTK_WIDGET_REALIZED(widget))
|
||||
return;
|
||||
|
||||
if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL)
|
||||
return;
|
||||
|
||||
gdk_drawable_get_size(icon_info->window, &width, &height);
|
||||
|
||||
if (width == 1 || height == 1)
|
||||
{
|
||||
/*
|
||||
* size_allocate hasn't been called yet. These are the default values.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (gdk_pixbuf_get_height(pixbuf) > height)
|
||||
{
|
||||
GdkPixbuf *temp_pixbuf;
|
||||
int scale;
|
||||
|
||||
scale = height - (2 * ICON_MARGIN);
|
||||
|
||||
temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale,
|
||||
GDK_INTERP_BILINEAR);
|
||||
|
||||
g_object_unref(pixbuf);
|
||||
|
||||
pixbuf = temp_pixbuf;
|
||||
}
|
||||
|
||||
x = (width - gdk_pixbuf_get_width(pixbuf)) / 2;
|
||||
y = (height - gdk_pixbuf_get_height(pixbuf)) / 2;
|
||||
|
||||
if (icon_info->hovered)
|
||||
{
|
||||
GdkPixbuf *temp_pixbuf;
|
||||
|
||||
temp_pixbuf = gdk_pixbuf_copy(pixbuf);
|
||||
|
||||
colorshift_pixbuf(temp_pixbuf, pixbuf, 30);
|
||||
|
||||
g_object_unref(pixbuf);
|
||||
|
||||
pixbuf = temp_pixbuf;
|
||||
}
|
||||
|
||||
gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf,
|
||||
0, 0, x, y, -1, -1,
|
||||
GDK_RGB_DITHER_NORMAL, 0, 0);
|
||||
|
||||
g_object_unref(pixbuf);
|
||||
}
|
||||
|
||||
static gint
|
||||
sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
|
||||
{
|
||||
SexyIconEntry *entry;
|
||||
|
||||
g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE);
|
||||
g_return_val_if_fail(event != NULL, FALSE);
|
||||
|
||||
entry = SEXY_ICON_ENTRY(widget);
|
||||
|
||||
if (GTK_WIDGET_DRAWABLE(widget))
|
||||
{
|
||||
gboolean found = FALSE;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ICONS && !found; i++)
|
||||
{
|
||||
SexyIconInfo *icon_info = &entry->priv->icons[i];
|
||||
|
||||
if (event->window == icon_info->window)
|
||||
{
|
||||
gint width;
|
||||
GtkAllocation text_area_alloc;
|
||||
|
||||
get_text_area_size(entry, &text_area_alloc);
|
||||
gdk_drawable_get_size(icon_info->window, &width, NULL);
|
||||
|
||||
gtk_paint_flat_box(widget->style, icon_info->window,
|
||||
GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
|
||||
NULL, widget, "entry_bg",
|
||||
0, 0, width, text_area_alloc.height);
|
||||
|
||||
draw_icon(widget, i);
|
||||
|
||||
found = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
|
||||
{
|
||||
if (param != NULL)
|
||||
{
|
||||
const char *name = g_param_spec_get_name(param);
|
||||
|
||||
if (strcmp(name, "pixbuf") && strcmp(name, "stock") &&
|
||||
strcmp(name, "image") && strcmp(name, "pixmap") &&
|
||||
strcmp(name, "icon_set") && strcmp(name, "pixbuf_animation"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_queue_resize(GTK_WIDGET(entry));
|
||||
}
|
||||
|
||||
static gint
|
||||
sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
if (event->window == entry->priv->icons[i].window)
|
||||
{
|
||||
if (sexy_icon_entry_get_icon_highlight(entry, i))
|
||||
{
|
||||
entry->priv->icons[i].hovered = TRUE;
|
||||
|
||||
update_icon(NULL, NULL, entry);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint
|
||||
sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
if (event->window == entry->priv->icons[i].window)
|
||||
{
|
||||
if (sexy_icon_entry_get_icon_highlight(entry, i))
|
||||
{
|
||||
entry->priv->icons[i].hovered = FALSE;
|
||||
|
||||
update_icon(NULL, NULL, entry);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint
|
||||
sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
if (event->window == entry->priv->icons[i].window)
|
||||
{
|
||||
if (event->button == 1 &&
|
||||
sexy_icon_entry_get_icon_highlight(entry, i))
|
||||
{
|
||||
entry->priv->icons[i].hovered = FALSE;
|
||||
|
||||
update_icon(NULL, NULL, entry);
|
||||
}
|
||||
|
||||
g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
|
||||
return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
|
||||
event);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint
|
||||
sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
GdkWindow *icon_window = entry->priv->icons[i].window;
|
||||
|
||||
if (event->window == icon_window)
|
||||
{
|
||||
int width, height;
|
||||
gdk_drawable_get_size(icon_window, &width, &height);
|
||||
|
||||
if (event->button == 1 &&
|
||||
sexy_icon_entry_get_icon_highlight(entry, i) &&
|
||||
event->x >= 0 && event->y >= 0 &&
|
||||
event->x <= width && event->y <= height)
|
||||
{
|
||||
entry->priv->icons[i].hovered = TRUE;
|
||||
|
||||
update_icon(NULL, NULL, entry);
|
||||
}
|
||||
|
||||
g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
|
||||
return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,
|
||||
event);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* sexy_icon_entry_new
|
||||
*
|
||||
* Creates a new SexyIconEntry widget.
|
||||
*
|
||||
* Returns a new #SexyIconEntry.
|
||||
*/
|
||||
GtkWidget *
|
||||
sexy_icon_entry_new(void)
|
||||
{
|
||||
return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* sexy_icon_entry_set_icon
|
||||
* @entry: A #SexyIconEntry.
|
||||
* @position: Icon position.
|
||||
* @icon: A #GtkImage to set as the icon.
|
||||
*
|
||||
* Sets the icon shown in the entry
|
||||
*/
|
||||
void
|
||||
sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
|
||||
GtkImage *icon)
|
||||
{
|
||||
SexyIconInfo *icon_info;
|
||||
|
||||
g_return_if_fail(entry != NULL);
|
||||
g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
|
||||
g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
|
||||
g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon));
|
||||
|
||||
icon_info = &entry->priv->icons[icon_pos];
|
||||
|
||||
if (icon == icon_info->icon)
|
||||
return;
|
||||
|
||||
if (icon_pos == SEXY_ICON_ENTRY_SECONDARY &&
|
||||
entry->priv->icon_released_id != 0)
|
||||
{
|
||||
g_signal_handler_disconnect(entry, entry->priv->icon_released_id);
|
||||
entry->priv->icon_released_id = 0;
|
||||
}
|
||||
|
||||
if (icon == NULL)
|
||||
{
|
||||
if (icon_info->icon != NULL)
|
||||
{
|
||||
gtk_widget_destroy(GTK_WIDGET(icon_info->icon));
|
||||
icon_info->icon = NULL;
|
||||
|
||||
/*
|
||||
* Explicitly check, as the pointer may become invalidated
|
||||
* during destruction.
|
||||
*/
|
||||
if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window))
|
||||
gdk_window_hide(icon_info->window);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (icon_info->window != NULL && icon_info->icon == NULL)
|
||||
gdk_window_show(icon_info->window);
|
||||
|
||||
g_signal_connect(G_OBJECT(icon), "notify",
|
||||
G_CALLBACK(update_icon), entry);
|
||||
|
||||
icon_info->icon = icon;
|
||||
g_object_ref(icon);
|
||||
}
|
||||
|
||||
update_icon(NULL, NULL, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* sexy_icon_entry_set_icon_highlight
|
||||
* @entry: A #SexyIconEntry;
|
||||
* @position: Icon position.
|
||||
* @highlight: TRUE if the icon should highlight on mouse-over
|
||||
*
|
||||
* Determines whether the icon will highlight on mouse-over.
|
||||
*/
|
||||
void
|
||||
sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
|
||||
SexyIconEntryPosition icon_pos,
|
||||
gboolean highlight)
|
||||
{
|
||||
SexyIconInfo *icon_info;
|
||||
|
||||
g_return_if_fail(entry != NULL);
|
||||
g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
|
||||
g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
|
||||
|
||||
icon_info = &entry->priv->icons[icon_pos];
|
||||
|
||||
if (icon_info->highlight == highlight)
|
||||
return;
|
||||
|
||||
icon_info->highlight = highlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* sexy_icon_entry_get_icon
|
||||
* @entry: A #SexyIconEntry.
|
||||
* @position: Icon position.
|
||||
*
|
||||
* Retrieves the image used for the icon
|
||||
*
|
||||
* Returns: A #GtkImage.
|
||||
*/
|
||||
GtkImage *
|
||||
sexy_icon_entry_get_icon(const SexyIconEntry *entry,
|
||||
SexyIconEntryPosition icon_pos)
|
||||
{
|
||||
g_return_val_if_fail(entry != NULL, NULL);
|
||||
g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL);
|
||||
g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL);
|
||||
|
||||
return entry->priv->icons[icon_pos].icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* sexy_icon_entry_get_icon_highlight
|
||||
* @entry: A #SexyIconEntry.
|
||||
* @position: Icon position.
|
||||
*
|
||||
* Retrieves whether entry will highlight the icon on mouseover.
|
||||
*
|
||||
* Returns: TRUE if icon highlights.
|
||||
*/
|
||||
gboolean
|
||||
sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
|
||||
SexyIconEntryPosition icon_pos)
|
||||
{
|
||||
g_return_val_if_fail(entry != NULL, FALSE);
|
||||
g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE);
|
||||
g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE);
|
||||
|
||||
return entry->priv->icons[icon_pos].highlight;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_button_clicked_cb(SexyIconEntry *icon_entry,
|
||||
SexyIconEntryPosition icon_pos,
|
||||
int button)
|
||||
{
|
||||
if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1)
|
||||
return;
|
||||
|
||||
gtk_entry_set_text(GTK_ENTRY(icon_entry), "");
|
||||
}
|
||||
|
||||
/**
|
||||
* sexy_icon_entry_add_clear_button
|
||||
* @icon_entry: A #SexyIconEntry.
|
||||
*
|
||||
* A convenience function to add a clear button to the end of the entry.
|
||||
* This is useful for search boxes.
|
||||
*/
|
||||
void
|
||||
sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry)
|
||||
{
|
||||
GtkWidget *icon;
|
||||
|
||||
g_return_if_fail(icon_entry != NULL);
|
||||
g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry));
|
||||
|
||||
icon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
|
||||
gtk_widget_show(icon);
|
||||
sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(icon_entry),
|
||||
SEXY_ICON_ENTRY_SECONDARY,
|
||||
GTK_IMAGE(icon));
|
||||
sexy_icon_entry_set_icon_highlight(SEXY_ICON_ENTRY(icon_entry),
|
||||
SEXY_ICON_ENTRY_SECONDARY, TRUE);
|
||||
|
||||
if (icon_entry->priv->icon_released_id != 0)
|
||||
{
|
||||
g_signal_handler_disconnect(icon_entry,
|
||||
icon_entry->priv->icon_released_id);
|
||||
}
|
||||
|
||||
icon_entry->priv->icon_released_id =
|
||||
g_signal_connect(G_OBJECT(icon_entry), "icon_released",
|
||||
G_CALLBACK(clear_button_clicked_cb), NULL);
|
||||
}
|
||||
|
||||
GType
|
||||
sexy_icon_entry_position_get_type (void)
|
||||
{
|
||||
static GType etype = 0;
|
||||
if (etype == 0) {
|
||||
static const GEnumValue values[] = {
|
||||
{ SEXY_ICON_ENTRY_PRIMARY, "SEXY_ICON_ENTRY_PRIMARY", "primary" },
|
||||
{ SEXY_ICON_ENTRY_SECONDARY, "SEXY_ICON_ENTRY_SECONDARY", "secondary" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
etype = g_enum_register_static ("SexyIconEntryPosition", values);
|
||||
}
|
||||
return etype;
|
||||
}
|
||||
|
104
lib/ui/sexy-icon-entry.h
Normal file
104
lib/ui/sexy-icon-entry.h
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* @file libsexy/sexy-icon-entry.h Entry widget
|
||||
*
|
||||
* @Copyright (C) 2004-2006 Christian Hammond.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _SEXY_ICON_ENTRY_H_
|
||||
#define _SEXY_ICON_ENTRY_H_
|
||||
|
||||
typedef struct _SexyIconEntry SexyIconEntry;
|
||||
typedef struct _SexyIconEntryClass SexyIconEntryClass;
|
||||
typedef struct _SexyIconEntryPriv SexyIconEntryPriv;
|
||||
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
|
||||
#define SEXY_TYPE_ICON_ENTRY (sexy_icon_entry_get_type())
|
||||
#define SEXY_ICON_ENTRY(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntry))
|
||||
#define SEXY_ICON_ENTRY_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
|
||||
#define SEXY_IS_ICON_ENTRY(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), SEXY_TYPE_ICON_ENTRY))
|
||||
#define SEXY_IS_ICON_ENTRY_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), SEXY_TYPE_ICON_ENTRY))
|
||||
#define SEXY_ICON_ENTRY_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SEXY_ICON_ENTRY_PRIMARY,
|
||||
SEXY_ICON_ENTRY_SECONDARY
|
||||
|
||||
} SexyIconEntryPosition;
|
||||
|
||||
GType sexy_icon_entry_position_get_type(void);
|
||||
#define SEXY_TYPE_ICON_ENTRY_POSITION (sexy_icon_entry_position_get_type())
|
||||
|
||||
struct _SexyIconEntry
|
||||
{
|
||||
GtkEntry parent_object;
|
||||
|
||||
SexyIconEntryPriv *priv;
|
||||
|
||||
void (*gtk_reserved1)(void);
|
||||
void (*gtk_reserved2)(void);
|
||||
void (*gtk_reserved3)(void);
|
||||
void (*gtk_reserved4)(void);
|
||||
};
|
||||
|
||||
struct _SexyIconEntryClass
|
||||
{
|
||||
GtkEntryClass parent_class;
|
||||
|
||||
/* Signals */
|
||||
void (*icon_pressed)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
|
||||
int button);
|
||||
void (*icon_released)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
|
||||
int button);
|
||||
|
||||
void (*gtk_reserved1)(void);
|
||||
void (*gtk_reserved2)(void);
|
||||
void (*gtk_reserved3)(void);
|
||||
void (*gtk_reserved4)(void);
|
||||
};
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GType sexy_icon_entry_get_type(void);
|
||||
|
||||
GtkWidget *sexy_icon_entry_new(void);
|
||||
|
||||
void sexy_icon_entry_set_icon(SexyIconEntry *entry,
|
||||
SexyIconEntryPosition position,
|
||||
GtkImage *icon);
|
||||
|
||||
void sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
|
||||
SexyIconEntryPosition position,
|
||||
gboolean highlight);
|
||||
|
||||
GtkImage *sexy_icon_entry_get_icon(const SexyIconEntry *entry,
|
||||
SexyIconEntryPosition position);
|
||||
|
||||
gboolean sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
|
||||
SexyIconEntryPosition position);
|
||||
void sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _SEXY_ICON_ENTRY_H_ */
|
121
po/mk.po
121
po/mk.po
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: olpc-sugar.master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2007-07-05 07:57-0700\n"
|
||||
"PO-Revision-Date: 2007-07-05 19:34+0200\n"
|
||||
"POT-Creation-Date: 2007-07-24 10:09-0700\n"
|
||||
"PO-Revision-Date: 2007-08-08 15:40+0200\n"
|
||||
"Last-Translator: Arangel Angov <arangel@linux.net.mk>\n"
|
||||
"Language-Team: Macedonian <ossm-members@hedona.on.net.mk>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -16,72 +16,129 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: KBabel 1.11.4\n"
|
||||
|
||||
#: ../shell/intro/intro.py:77
|
||||
msgid "Pick a buddy picture"
|
||||
msgstr "Избери слика за пријател"
|
||||
#: ../shell/intro/intro.py:61
|
||||
msgid "Name:"
|
||||
msgstr "Име:"
|
||||
|
||||
#: ../shell/intro/intro.py:100
|
||||
msgid "My Picture:"
|
||||
msgstr "Мојата слика:"
|
||||
#: ../shell/intro/intro.py:84
|
||||
msgid "Click to change color:"
|
||||
msgstr "Кликни да смениш боја:"
|
||||
|
||||
#: ../shell/intro/intro.py:180
|
||||
msgid "My Name:"
|
||||
msgstr "Моето име:"
|
||||
#: ../shell/intro/intro.py:134
|
||||
msgid "Back"
|
||||
msgstr "Назад"
|
||||
|
||||
#: ../shell/intro/intro.py:204
|
||||
msgid "My Color:"
|
||||
msgstr "Мојата боја:"
|
||||
#: ../shell/intro/intro.py:142
|
||||
msgid "Done"
|
||||
msgstr "Завршено"
|
||||
|
||||
#: ../shell/view/BuddyMenu.py:83
|
||||
#: ../shell/intro/intro.py:145
|
||||
msgid "Next"
|
||||
msgstr "Напред"
|
||||
|
||||
#: ../shell/view/BuddyMenu.py:82
|
||||
msgid "Remove friend"
|
||||
msgstr "Отстрани пријател"
|
||||
|
||||
#: ../shell/view/BuddyMenu.py:87
|
||||
#: ../shell/view/BuddyMenu.py:85
|
||||
msgid "Make friend"
|
||||
msgstr "Додај пријател"
|
||||
|
||||
#: ../shell/view/BuddyMenu.py:97
|
||||
#. FIXME check that the buddy is not in the activity already
|
||||
#: ../shell/view/BuddyMenu.py:96
|
||||
msgid "Invite"
|
||||
msgstr "Покани"
|
||||
|
||||
#: ../shell/view/clipboardmenu.py:103
|
||||
#: ../shell/view/clipboardmenu.py:66
|
||||
msgid "Remove"
|
||||
msgstr "Отстрани"
|
||||
|
||||
#: ../shell/view/clipboardmenu.py:110
|
||||
#: ../shell/view/clipboardmenu.py:70
|
||||
msgid "Open"
|
||||
msgstr "Отвори"
|
||||
|
||||
#: ../shell/view/clipboardmenu.py:117
|
||||
msgid "Stop download"
|
||||
msgstr "Прекини преземање"
|
||||
|
||||
#: ../shell/view/clipboardmenu.py:124
|
||||
#. self._stop_item = MenuItem(_('Stop download'), 'stock-close')
|
||||
#. TODO: Implement stopping downloads
|
||||
#. self._stop_item.connect('activate', self._stop_item_activate_cb)
|
||||
#. self.append_menu_item(self._stop_item)
|
||||
#: ../shell/view/clipboardmenu.py:79
|
||||
msgid "Add to journal"
|
||||
msgstr "Додај во дневникот"
|
||||
|
||||
#: ../shell/view/clipboardmenu.py:180
|
||||
#, python-format
|
||||
msgid "Clipboard object: %s."
|
||||
msgstr "Објект од таблата со исечоци: %s"
|
||||
|
||||
#: ../shell/view/frame/zoombox.py:42
|
||||
msgid "Neighborhood"
|
||||
msgstr "Соседство"
|
||||
|
||||
#: ../shell/view/frame/zoombox.py:55
|
||||
msgid "Group"
|
||||
msgstr "Група"
|
||||
|
||||
#: ../shell/view/frame/zoombox.py:68
|
||||
msgid "Home"
|
||||
msgstr "Дома"
|
||||
|
||||
#: ../shell/view/frame/zoombox.py:81
|
||||
msgid "Activity"
|
||||
msgstr "Активност"
|
||||
|
||||
#: ../services/clipboard/objecttypeservice.py:32
|
||||
msgid "Text"
|
||||
msgstr "Текст"
|
||||
|
||||
#: ../services/clipboard/objecttypeservice.py:35
|
||||
#: ../services/clipboard/objecttypeservice.py:36
|
||||
msgid "Image"
|
||||
msgstr "Слика"
|
||||
|
||||
#: ../shell/view/Shell.py:227
|
||||
#: ../shell/view/Shell.py:203
|
||||
msgid "Screenshot"
|
||||
msgstr "Слика од екранот"
|
||||
|
||||
#: ../shell/view/clipboardicon.py:211
|
||||
#, python-format
|
||||
msgid "Clipboard object: %s."
|
||||
msgstr "Објект од таблата со исечоци: %s"
|
||||
#: ../shell/view/home/HomeBox.py:140
|
||||
msgid "Shutdown"
|
||||
msgstr "Исклучи"
|
||||
|
||||
#: ../shell/view/home/MeshBox.py:122
|
||||
#: ../shell/view/home/MeshBox.py:126
|
||||
msgid "Mesh Network"
|
||||
msgstr "Соседство"
|
||||
|
||||
#: ../sugar/activity/activity.py:232
|
||||
#: ../shell/view/devices/battery.py:34
|
||||
msgid "My Battery life"
|
||||
msgstr "Мојата батерија"
|
||||
|
||||
#: ../shell/view/devices/battery.py:87
|
||||
msgid "Battery charging"
|
||||
msgstr "Батеријата се полни"
|
||||
|
||||
#: ../shell/view/devices/battery.py:89
|
||||
msgid "Battery discharging"
|
||||
msgstr "Батерјате се празни"
|
||||
|
||||
#: ../shell/view/devices/battery.py:91
|
||||
msgid "Battery fully charged"
|
||||
msgstr "Батеријата е наполнета"
|
||||
|
||||
#: ../sugar/activity/activity.py:73
|
||||
msgid "Private"
|
||||
msgstr "Приватно"
|
||||
|
||||
#: ../sugar/activity/activity.py:74
|
||||
msgid "My Neighborhood"
|
||||
msgstr "Мое соседство"
|
||||
|
||||
#: ../sugar/activity/activity.py:81
|
||||
msgid "Keep"
|
||||
msgstr "Зачувај"
|
||||
|
||||
#: ../sugar/activity/activity.py:87
|
||||
msgid "Stop"
|
||||
msgstr "Стоп"
|
||||
|
||||
#: ../sugar/activity/activity.py:260
|
||||
#, python-format
|
||||
msgid "%s Activity"
|
||||
msgstr "%s активност"
|
||||
|
@ -79,7 +79,7 @@ class Service(dbus.service.Object):
|
||||
self._console = Console()
|
||||
|
||||
@dbus.service.method(CONSOLE_IFACE)
|
||||
def toggle_visibility(self):
|
||||
def ToggleVisibility(self):
|
||||
window = self._console.window
|
||||
if not window.props.visible:
|
||||
window.present()
|
||||
|
@ -69,16 +69,16 @@ class BundleRegistry(gobject.GObject):
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self._bundles = {}
|
||||
self._bundles = []
|
||||
self._search_path = []
|
||||
self._service_manager = _ServiceManager()
|
||||
|
||||
def get_bundle(self, service_name):
|
||||
"""Returns an bundle given his service name"""
|
||||
if self._bundles.has_key(service_name):
|
||||
return self._bundles[service_name]
|
||||
else:
|
||||
return None
|
||||
for bundle in self._bundles:
|
||||
if bundle.get_service_name() == service_name:
|
||||
return bundle
|
||||
return None
|
||||
|
||||
def add_search_path(self, path):
|
||||
"""Add a directory to the bundles search path"""
|
||||
@ -86,20 +86,30 @@ class BundleRegistry(gobject.GObject):
|
||||
self._scan_directory(path)
|
||||
|
||||
def __iter__(self):
|
||||
return self._bundles.values().__iter__()
|
||||
return self._bundles.__iter__()
|
||||
|
||||
def _scan_directory(self, path):
|
||||
if os.path.isdir(path):
|
||||
for f in os.listdir(path):
|
||||
bundle_dir = os.path.join(path, f)
|
||||
if os.path.isdir(bundle_dir) and \
|
||||
bundle_dir.endswith('.activity'):
|
||||
self.add_bundle(bundle_dir)
|
||||
if not os.path.isdir(path):
|
||||
return
|
||||
|
||||
# Sort by mtime to ensure a stable activity order
|
||||
bundles = {}
|
||||
for f in os.listdir(path):
|
||||
if not f.endswith('.activity'):
|
||||
continue
|
||||
bundle_dir = os.path.join(path, f)
|
||||
if os.path.isdir(bundle_dir):
|
||||
bundles[bundle_dir] = os.stat(bundle_dir).st_mtime
|
||||
|
||||
bundle_dirs = bundles.keys()
|
||||
bundle_dirs.sort(lambda d1,d2: cmp(bundles[d1], bundles[d2]))
|
||||
for dir in bundle_dirs:
|
||||
self.add_bundle(dir)
|
||||
|
||||
def add_bundle(self, bundle_path):
|
||||
bundle = Bundle(bundle_path)
|
||||
if bundle.is_valid():
|
||||
self._bundles[bundle.get_service_name()] = bundle
|
||||
self._bundles.append(bundle)
|
||||
self._service_manager.add(bundle)
|
||||
self.emit('bundle-added', bundle)
|
||||
return True
|
||||
@ -108,7 +118,7 @@ class BundleRegistry(gobject.GObject):
|
||||
|
||||
def get_activities_for_type(self, mime_type):
|
||||
result = []
|
||||
for bundle in self._bundles.values():
|
||||
for bundle in self._bundles:
|
||||
if bundle.get_mime_types() and mime_type in bundle.get_mime_types():
|
||||
result.append(bundle)
|
||||
return result
|
||||
|
@ -65,16 +65,15 @@ class ClipboardObject:
|
||||
#return self._get_type_info().get_preview()
|
||||
return ''
|
||||
|
||||
def get_activity(self):
|
||||
def get_activities(self):
|
||||
mime = self.get_mime_type()
|
||||
if not mime:
|
||||
return ''
|
||||
|
||||
registry = bundleregistry.get_registry()
|
||||
activities = registry.get_activities_for_type(self.get_mime_type())
|
||||
# TODO: should we return several activities?
|
||||
if activities:
|
||||
return activities[0].get_service_name()
|
||||
return [activity.get_service_name() for activity in activities]
|
||||
else:
|
||||
return ''
|
||||
|
||||
@ -102,8 +101,11 @@ class ClipboardObject:
|
||||
if len(uris) == 1 or not uris[1]:
|
||||
uri = urlparse.urlparse(uris[0], 'file')
|
||||
if uri.scheme == 'file':
|
||||
logging.debug('Choosed %r!' % mime.get_for_file(uri.path))
|
||||
format = mime.get_for_file(uri.path)
|
||||
if os.path.exists(uri.path):
|
||||
format = mime.get_for_file(uri.path)
|
||||
else:
|
||||
format = mime.get_from_file_name(uri.path)
|
||||
logging.debug('Choosed %r!' % format)
|
||||
|
||||
return format
|
||||
|
||||
|
@ -33,7 +33,7 @@ NAME_KEY = 'NAME'
|
||||
PERCENT_KEY = 'PERCENT'
|
||||
ICON_KEY = 'ICON'
|
||||
PREVIEW_KEY = 'PREVIEW'
|
||||
ACTIVITY_KEY = 'ACTIVITY'
|
||||
ACTIVITIES_KEY = 'ACTIVITIES'
|
||||
FORMATS_KEY = 'FORMATS'
|
||||
|
||||
TYPE_KEY = 'TYPE'
|
||||
@ -87,7 +87,7 @@ class ClipboardService(dbus.service.Object):
|
||||
PERCENT_KEY: cb_object.get_percent(),
|
||||
ICON_KEY: cb_object.get_icon(),
|
||||
PREVIEW_KEY: cb_object.get_preview(),
|
||||
ACTIVITY_KEY: cb_object.get_activity()})
|
||||
ACTIVITIES_KEY: cb_object.get_activities()})
|
||||
|
||||
@dbus.service.method(_CLIPBOARD_DBUS_INTERFACE,
|
||||
in_signature="o", out_signature="")
|
||||
@ -121,7 +121,7 @@ class ClipboardService(dbus.service.Object):
|
||||
PERCENT_KEY: percent,
|
||||
ICON_KEY: cb_object.get_icon(),
|
||||
PREVIEW_KEY: cb_object.get_preview(),
|
||||
ACTIVITY_KEY: cb_object.get_activity()})
|
||||
ACTIVITIES_KEY: cb_object.get_activities()})
|
||||
|
||||
@dbus.service.method(_CLIPBOARD_DBUS_INTERFACE,
|
||||
in_signature="o", out_signature="a{sv}")
|
||||
@ -142,7 +142,7 @@ class ClipboardService(dbus.service.Object):
|
||||
PERCENT_KEY: cb_object.get_percent(),
|
||||
ICON_KEY: cb_object.get_icon(),
|
||||
PREVIEW_KEY: cb_object.get_preview(),
|
||||
ACTIVITY_KEY: cb_object.get_activity(),
|
||||
ACTIVITIES_KEY: cb_object.get_activities(),
|
||||
FORMATS_KEY: format_types}
|
||||
return dbus.Dictionary(result_dict)
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
||||
import dbus
|
||||
import dbus.service
|
||||
|
||||
from gettext import gettext as _
|
||||
|
||||
_REGISTRY_IFACE = "org.laptop.ObjectTypeRegistry"
|
||||
_REGISTRY_PATH = "/org/laptop/ObjectTypeRegistry"
|
||||
|
||||
@ -28,13 +30,15 @@ class ObjectTypeRegistry(dbus.service.Object):
|
||||
|
||||
self._types = {}
|
||||
|
||||
from gettext import gettext as _
|
||||
self._add_primitive('Text', _('Text'), 'theme:object-text',
|
||||
self._add_primitive('Text', _('Text'), 'theme:text',
|
||||
[ 'text/plain', 'text/rtf', 'application/pdf',
|
||||
'application/x-pdf', 'text/html',
|
||||
'application/vnd.oasis.opendocument.text' ])
|
||||
self._add_primitive('Image', _('Image'), 'theme:object-image',
|
||||
'application/vnd.oasis.opendocument.text',
|
||||
'application/rtf', 'text/rtf' ])
|
||||
self._add_primitive('Image', _('Image'), 'theme:image',
|
||||
[ 'image/png', 'image/gif', 'image/jpeg' ])
|
||||
self._add_primitive('Audio', _('Audio'), 'theme:audio', [ 'audio/ogg' ])
|
||||
self._add_primitive('Video', _('Video'), 'theme:video', [ 'video/ogg' ])
|
||||
|
||||
def _add_primitive(self, type_id, name, icon, mime_types):
|
||||
object_type = {'type_id': type_id,
|
||||
|
@ -26,7 +26,7 @@ class ColorPicker(hippo.CanvasBox, hippo.CanvasItem):
|
||||
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
|
||||
|
||||
self._xo = CanvasIcon(size=style.XLARGE_ICON_SIZE,
|
||||
icon_name='theme:stock-buddy')
|
||||
icon_name='theme:xo')
|
||||
self._set_random_colors()
|
||||
self._xo.connect('activated', self._xo_activated_cb)
|
||||
self.append(self._xo)
|
||||
|
@ -103,7 +103,7 @@ class HomeActivity(gobject.GObject):
|
||||
if self._activity_info:
|
||||
return self._activity_info.icon
|
||||
else:
|
||||
return 'theme:stock-missing'
|
||||
return 'theme:image-missing'
|
||||
|
||||
def get_icon_color(self):
|
||||
"""Retrieve the appropriate icon colour for this activity
|
||||
|
@ -107,15 +107,15 @@ class HomeModel(gobject.GObject):
|
||||
if self._active_activity:
|
||||
service = self._active_activity.get_service()
|
||||
if service:
|
||||
service.set_active(False,
|
||||
reply_handler=self._set_active_success,
|
||||
error_handler=self._set_active_error)
|
||||
service.SetActive(False,
|
||||
reply_handler=self._set_active_success,
|
||||
error_handler=self._set_active_error)
|
||||
if home_activity:
|
||||
service = home_activity.get_service()
|
||||
if service:
|
||||
service.set_active(True,
|
||||
reply_handler=self._set_active_success,
|
||||
error_handler=self._set_active_error)
|
||||
service.SetActive(True,
|
||||
reply_handler=self._set_active_success,
|
||||
error_handler=self._set_active_error)
|
||||
|
||||
self._active_activity = home_activity
|
||||
self.emit('active-activity-changed', self._active_activity)
|
||||
|
@ -20,7 +20,7 @@ from view.BuddyMenu import BuddyMenu
|
||||
|
||||
class BuddyIcon(CanvasIcon):
|
||||
def __init__(self, shell, buddy):
|
||||
CanvasIcon.__init__(self, icon_name='theme:stock-buddy',
|
||||
CanvasIcon.__init__(self, icon_name='theme:xo',
|
||||
xo_color=buddy.get_color())
|
||||
|
||||
self._shell = shell
|
||||
|
@ -80,10 +80,10 @@ class BuddyMenu(Palette):
|
||||
|
||||
friends = shell_model.get_friends()
|
||||
if friends.has_buddy(self._buddy):
|
||||
menu_item = MenuItem(_('Remove friend'), 'stock-remove')
|
||||
menu_item = MenuItem(_('Remove friend'), 'list-remove')
|
||||
menu_item.connect('activate', self._remove_friend_cb)
|
||||
else:
|
||||
menu_item = MenuItem(_('Make friend'), 'stock-add')
|
||||
menu_item = MenuItem(_('Make friend'), 'list-add')
|
||||
menu_item.connect('activate', self._make_friend_cb)
|
||||
|
||||
self.menu.append(menu_item)
|
||||
|
@ -88,7 +88,7 @@ class ClipboardIcon(CanvasIcon):
|
||||
if icon_name:
|
||||
self.props.icon_name = icon_name
|
||||
else:
|
||||
self.props.icon_name = 'theme:object-generic'
|
||||
self.props.icon_name = 'theme:unknown-object'
|
||||
|
||||
self._name = name
|
||||
self._percent = percent
|
||||
|
@ -30,16 +30,17 @@ from sugar.clipboard import clipboardservice
|
||||
from sugar.datastore import datastore
|
||||
from sugar.objects import mime
|
||||
from sugar import profile
|
||||
from sugar import activity
|
||||
|
||||
class ClipboardMenu(Palette):
|
||||
|
||||
def __init__(self, object_id, name, percent, preview, activity, installable):
|
||||
def __init__(self, object_id, name, percent, preview, activities, installable):
|
||||
Palette.__init__(self, name)
|
||||
|
||||
self.props.position = Palette.RIGHT
|
||||
self._object_id = object_id
|
||||
self._percent = percent
|
||||
self._activity = activity
|
||||
self._activities = activities
|
||||
|
||||
self.set_group_id('frame')
|
||||
|
||||
@ -62,13 +63,14 @@ class ClipboardMenu(Palette):
|
||||
self.append(self._preview_text)
|
||||
"""
|
||||
|
||||
self._remove_item = MenuItem(_('Remove'), 'stock-remove')
|
||||
self._remove_item = MenuItem(_('Remove'), 'list-remove')
|
||||
self._remove_item.connect('activate', self._remove_item_activate_cb)
|
||||
self.menu.append(self._remove_item)
|
||||
self._remove_item.show()
|
||||
|
||||
self._open_item = MenuItem(_('Open'), 'stock-keep')
|
||||
self._open_item.connect('activate', self._open_item_activate_cb)
|
||||
self._open_item = MenuItem(_('Open'))
|
||||
self._open_item_activate_sid = self._open_item.connect('activate',
|
||||
self._open_item_activate_cb)
|
||||
self.menu.append(self._open_item)
|
||||
self._open_item.show()
|
||||
|
||||
@ -83,14 +85,49 @@ class ClipboardMenu(Palette):
|
||||
self._journal_item.show()
|
||||
|
||||
self._update_items_visibility(installable)
|
||||
self._update_open_submenu()
|
||||
|
||||
def _update_open_submenu(self):
|
||||
submenu = self._open_item.get_submenu()
|
||||
if submenu:
|
||||
for item in submenu.get_children():
|
||||
submenu.remove(item)
|
||||
|
||||
if self._activities is None or len(self._activities) <= 1:
|
||||
if self._open_item_activate_sid is None:
|
||||
self._open_item_activate_sid = self._open_item.connect(
|
||||
'activate',
|
||||
self._open_item_activate_cb)
|
||||
return
|
||||
else:
|
||||
if self._open_item_activate_sid is not None:
|
||||
self._open_item.disconnect(self._open_item_activate_sid)
|
||||
self._open_item_activate_sid = None
|
||||
|
||||
if not submenu:
|
||||
submenu = gtk.Menu()
|
||||
self._open_item.set_submenu(submenu)
|
||||
submenu.show()
|
||||
|
||||
for service_name in self._activities:
|
||||
registry = activity.get_registry()
|
||||
activity_info = registry.get_activity(service_name)
|
||||
|
||||
if not activity_info:
|
||||
logging.warning('Activity %s is unknown.' % service_name)
|
||||
|
||||
item = gtk.MenuItem(activity_info.name)
|
||||
item.connect('activate', self._open_submenu_item_activate_cb, service_name)
|
||||
submenu.append(item)
|
||||
item.show()
|
||||
|
||||
def _update_items_visibility(self, installable):
|
||||
if self._percent == 100 and (self._activity or installable):
|
||||
if self._percent == 100 and (self._activities or installable):
|
||||
self._remove_item.props.sensitive = True
|
||||
self._open_item.props.sensitive = True
|
||||
#self._stop_item.props.sensitive = False
|
||||
self._journal_item.props.sensitive = True
|
||||
elif self._percent == 100 and (not self._activity and not installable):
|
||||
elif self._percent == 100 and (not self._activities and not installable):
|
||||
self._remove_item.props.sensitive = True
|
||||
self._open_item.props.sensitive = False
|
||||
#self._stop_item.props.sensitive = False
|
||||
@ -112,26 +149,36 @@ class ClipboardMenu(Palette):
|
||||
self._progress_bar.props.fraction = self._percent / 100.0
|
||||
self._progress_bar.props.text = '%.2f %%' % self._percent
|
||||
|
||||
def set_state(self, name, percent, preview, activity, installable):
|
||||
def set_state(self, name, percent, preview, activities, installable):
|
||||
self.set_primary_text(name)
|
||||
self._percent = percent
|
||||
self._activity = activity
|
||||
self._activities = activities
|
||||
if self._progress_bar:
|
||||
self._update_progress_bar()
|
||||
self._update_items_visibility(installable)
|
||||
self._update_open_submenu()
|
||||
|
||||
def _open_item_activate_cb(self, menu_item):
|
||||
if self._percent < 100:
|
||||
return
|
||||
jobject = self._copy_to_journal()
|
||||
jobject.resume()
|
||||
jobject.resume(self._activities[0])
|
||||
jobject.destroy()
|
||||
|
||||
def _open_submenu_item_activate_cb(self, menu_item, service_name):
|
||||
if self._percent < 100:
|
||||
return
|
||||
jobject = self._copy_to_journal()
|
||||
jobject.resume(service_name)
|
||||
jobject.destroy()
|
||||
|
||||
def _remove_item_activate_cb(self, menu_item):
|
||||
cb_service = clipboardservice.get_instance()
|
||||
cb_service.delete_object(self._object_id)
|
||||
|
||||
def _journal_item_activate_cb(self, menu_item):
|
||||
self._copy_to_journal()
|
||||
jobject = self._copy_to_journal()
|
||||
jobject.destroy()
|
||||
|
||||
def _copy_to_journal(self):
|
||||
cb_service = clipboardservice.get_instance()
|
||||
|
@ -21,7 +21,7 @@ from sugar.graphics import canvasicon
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.palette import Palette
|
||||
|
||||
_ICON_NAME = 'device-battery'
|
||||
_ICON_NAME = 'battery'
|
||||
|
||||
_STATUS_CHARGING = 0
|
||||
_STATUS_DISCHARGING = 1
|
||||
|
@ -22,7 +22,7 @@ from model.devices import device
|
||||
class DeviceView(canvasicon.CanvasIcon):
|
||||
def __init__(self, model):
|
||||
canvasicon.CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE,
|
||||
icon_name='theme:device-network-mesh')
|
||||
icon_name='theme:network-mesh')
|
||||
self._model = model
|
||||
|
||||
model.connect('notify::state', self._state_changed_cb)
|
||||
|
@ -19,4 +19,4 @@ from view.devices import deviceview
|
||||
class DeviceView(deviceview.DeviceView):
|
||||
def __init__(self, model):
|
||||
deviceview.DeviceView.__init__(self, model)
|
||||
self.props.icon_name = 'theme:stock-net-wired'
|
||||
self.props.icon_name = 'theme:network-wired'
|
||||
|
@ -22,7 +22,7 @@ from model.devices.network import wireless
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from model.devices import device
|
||||
|
||||
_ICON_NAME = 'device-network-wireless'
|
||||
_ICON_NAME = 'network-wireless'
|
||||
|
||||
class DeviceView(CanvasIcon):
|
||||
def __init__(self, model):
|
||||
|
@ -16,22 +16,29 @@
|
||||
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.palette import Palette
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from sugar.graphics import style
|
||||
from sugar.presence import presenceservice
|
||||
|
||||
from view.BuddyIcon import BuddyIcon
|
||||
from model.BuddyModel import BuddyModel
|
||||
from view.frame.frameinvoker import FrameCanvasInvoker
|
||||
|
||||
class FriendIcon(BuddyIcon):
|
||||
def __init__(self, shell, buddy):
|
||||
BuddyIcon.__init__(self, shell, buddy)
|
||||
self.get_palette().set_group_id('frame')
|
||||
|
||||
palette = self.get_palette()
|
||||
palette.set_group_id('frame')
|
||||
palette.props.position = Palette.AROUND
|
||||
palette.props.invoker = FrameCanvasInvoker(self)
|
||||
|
||||
def prelight(self, enter):
|
||||
if enter:
|
||||
self.props.background_color = style.COLOR_BLACK.get_int()
|
||||
else:
|
||||
self.props.background_color = style.COLOR_TOOLBAR.GREY.get_int()
|
||||
self.props.background_color = style.COLOR_TOOLBAR_GREY.get_int()
|
||||
|
||||
class FriendsBox(hippo.CanvasBox):
|
||||
def __init__(self, shell):
|
||||
|
@ -75,7 +75,7 @@ class ClipboardBox(hippo.CanvasBox):
|
||||
return self._owns_clipboard
|
||||
|
||||
def _get_icon_at_coords(self, x, y):
|
||||
box_x, box_y = self.get_context().get_position(self)
|
||||
box_x, box_y = self.get_context().translate_to_widget(self)
|
||||
x -= box_x
|
||||
y -= box_y
|
||||
for object_id, icon in self._icons.iteritems():
|
||||
@ -92,7 +92,7 @@ class ClipboardBox(hippo.CanvasBox):
|
||||
if not selection.data:
|
||||
return
|
||||
|
||||
logging.debug('ClipboardBox: adding type ' + selection.type + ' ' + selection.data)
|
||||
logging.debug('ClipboardBox: adding type ' + selection.type)
|
||||
|
||||
cb_service = clipboardservice.get_instance()
|
||||
if selection.type == 'text/uri-list':
|
||||
|
@ -36,10 +36,8 @@ class ClipboardPanelWindow(FrameWindow):
|
||||
self._clipboard = gtk.Clipboard()
|
||||
self._clipboard.connect("owner-change", self._owner_change_cb)
|
||||
|
||||
root = self.get_root()
|
||||
|
||||
self._clipboard_box = ClipboardBox()
|
||||
root.append(self._clipboard_box)
|
||||
self.append(self._clipboard_box)
|
||||
|
||||
# Receiving dnd drops
|
||||
self.drag_dest_set(0, [], 0)
|
||||
|
@ -208,11 +208,10 @@ class Frame(object):
|
||||
self._right_panel.hover)
|
||||
|
||||
def _create_top_panel(self):
|
||||
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
||||
root = panel.get_root()
|
||||
panel = self._create_panel(gtk.POS_TOP)
|
||||
|
||||
box = ZoomBox(self._shell)
|
||||
root.append(box)
|
||||
panel.append(box)
|
||||
|
||||
#box = OverlayBox(self._shell)
|
||||
#root.append(box, hippo.PACK_END)
|
||||
@ -220,25 +219,23 @@ class Frame(object):
|
||||
return panel
|
||||
|
||||
def _create_bottom_panel(self):
|
||||
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
||||
root = panel.get_root()
|
||||
panel = self._create_panel(gtk.POS_BOTTOM)
|
||||
|
||||
box = ActivitiesBox(self._shell)
|
||||
root.append(box)
|
||||
panel.append(box)
|
||||
|
||||
return panel
|
||||
|
||||
def _create_right_panel(self):
|
||||
panel = self._create_panel(hippo.ORIENTATION_VERTICAL)
|
||||
root = panel.get_root()
|
||||
panel = self._create_panel(gtk.POS_RIGHT)
|
||||
|
||||
box = FriendsBox(self._shell)
|
||||
root.append(box)
|
||||
panel.append(box)
|
||||
|
||||
return panel
|
||||
|
||||
def _create_left_panel(self):
|
||||
panel = ClipboardPanelWindow(self, hippo.ORIENTATION_VERTICAL)
|
||||
panel = ClipboardPanelWindow(self, gtk.POS_LEFT)
|
||||
|
||||
self._connect_to_panel(panel)
|
||||
panel.connect('drag-motion', self._drag_motion_cb)
|
||||
@ -271,16 +268,16 @@ class Frame(object):
|
||||
screen_w = gtk.gdk.screen_width()
|
||||
|
||||
self._move_panel(self._top_panel, self._current_position,
|
||||
0, - style.GRID_CELL_SIZE, 0, 0)
|
||||
0, - self._top_panel.size, 0, 0)
|
||||
|
||||
self._move_panel(self._bottom_panel, self._current_position,
|
||||
0, screen_h, 0, screen_h - style.GRID_CELL_SIZE)
|
||||
0, screen_h, 0, screen_h - self._bottom_panel.size)
|
||||
|
||||
self._move_panel(self._left_panel, self._current_position,
|
||||
- style.GRID_CELL_SIZE, 0, 0, 0)
|
||||
- self._left_panel.size, 0, 0, 0)
|
||||
|
||||
self._move_panel(self._right_panel, self._current_position,
|
||||
screen_w, 0, screen_w - style.GRID_CELL_SIZE, 0)
|
||||
screen_w, 0, screen_w - self._right_panel.size, 0)
|
||||
|
||||
def _hide_completed_cb(self, animator):
|
||||
self.mode = MODE_NONE
|
||||
|
@ -29,7 +29,7 @@ class FrameCanvasInvoker(CanvasInvoker):
|
||||
return Palette.AROUND
|
||||
|
||||
def get_screen_area(self):
|
||||
frame_thickness = style.zoom(75)
|
||||
frame_thickness = style.GRID_CELL_SIZE
|
||||
|
||||
x = y = frame_thickness
|
||||
width = gtk.gdk.screen_width() - frame_thickness
|
||||
|
@ -21,11 +21,13 @@ from sugar.graphics import style
|
||||
|
||||
class FrameWindow(gtk.Window):
|
||||
__gtype_name__ = 'SugarFrameWindow'
|
||||
def __init__(self, orientation):
|
||||
|
||||
def __init__(self, position):
|
||||
gtk.Window.__init__(self)
|
||||
self.hover = False
|
||||
self.size = style.GRID_CELL_SIZE + style.LINE_WIDTH
|
||||
|
||||
self._orientation = orientation
|
||||
self._position = position
|
||||
|
||||
self.set_decorated(False)
|
||||
self.connect('realize', self._realize_cb)
|
||||
@ -36,36 +38,55 @@ class FrameWindow(gtk.Window):
|
||||
self.add(self._canvas)
|
||||
self._canvas.show()
|
||||
|
||||
self._bg = hippo.CanvasBox(orientation=self._orientation)
|
||||
self._canvas.set_root(self._bg)
|
||||
box = hippo.CanvasBox()
|
||||
self._canvas.set_root(box)
|
||||
|
||||
padding = style.GRID_CELL_SIZE
|
||||
if self._position == gtk.POS_TOP or self._position == gtk.POS_BOTTOM:
|
||||
box.props.orientation = hippo.ORIENTATION_HORIZONTAL
|
||||
box.props.padding_left = padding
|
||||
box.props.padding_right = padding
|
||||
box.props.padding_top = 0
|
||||
box.props.padding_bottom = 0
|
||||
else:
|
||||
box.props.orientation = hippo.ORIENTATION_VERTICAL
|
||||
box.props.padding_left = 0
|
||||
box.props.padding_right = 0
|
||||
box.props.padding_top = padding
|
||||
box.props.padding_bottom = padding
|
||||
|
||||
self._bg = hippo.CanvasBox(
|
||||
border_color=style.COLOR_BUTTON_GREY.get_int())
|
||||
|
||||
border = style.LINE_WIDTH
|
||||
if position == gtk.POS_TOP:
|
||||
self._bg.props.orientation = hippo.ORIENTATION_HORIZONTAL
|
||||
self._bg.props.border_bottom = border
|
||||
elif position == gtk.POS_BOTTOM:
|
||||
self._bg.props.orientation = hippo.ORIENTATION_HORIZONTAL
|
||||
self._bg.props.border_top = border
|
||||
elif position == gtk.POS_LEFT:
|
||||
self._bg.props.orientation = hippo.ORIENTATION_VERTICAL
|
||||
self._bg.props.border_right = border
|
||||
elif position == gtk.POS_RIGHT:
|
||||
self._bg.props.orientation = hippo.ORIENTATION_VERTICAL
|
||||
self._bg.props.border_left = border
|
||||
|
||||
box.append(self._bg, hippo.PACK_EXPAND)
|
||||
|
||||
self._update_size()
|
||||
|
||||
screen = gtk.gdk.screen_get_default()
|
||||
screen.connect('size-changed', self._size_changed_cb)
|
||||
|
||||
def get_root(self):
|
||||
return self._bg
|
||||
|
||||
def append(self, child, flags=0):
|
||||
self._bg.append(child, flags)
|
||||
|
||||
def _update_size(self):
|
||||
padding = style.GRID_CELL_SIZE
|
||||
if self._orientation == hippo.ORIENTATION_HORIZONTAL:
|
||||
self._bg.props.padding_left = padding
|
||||
self._bg.props.padding_right = padding
|
||||
self._bg.props.padding_top = 0
|
||||
self._bg.props.padding_bottom = 0
|
||||
|
||||
width = gtk.gdk.screen_width()
|
||||
height = style.GRID_CELL_SIZE
|
||||
if self._position == gtk.POS_TOP or self._position == gtk.POS_BOTTOM:
|
||||
self.resize(gtk.gdk.screen_width(), self.size)
|
||||
else:
|
||||
self._bg.props.padding_left = 0
|
||||
self._bg.props.padding_right = 0
|
||||
self._bg.props.padding_top = padding
|
||||
self._bg.props.padding_bottom = padding
|
||||
|
||||
width = style.GRID_CELL_SIZE
|
||||
height = gtk.gdk.screen_height()
|
||||
self.resize(width, height)
|
||||
self.resize(self.size, gtk.gdk.screen_height())
|
||||
|
||||
def _realize_cb(self, widget):
|
||||
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||
|
@ -63,6 +63,11 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
|
||||
devices_model.connect('device-disappeared',
|
||||
self._device_disappeared_cb)
|
||||
|
||||
self._redraw_id = None
|
||||
|
||||
def __del__(self):
|
||||
self.suspend()
|
||||
|
||||
def _add_device(self, device):
|
||||
view = deviceview.create(device)
|
||||
self.append(view, hippo.PACK_FIXED)
|
||||
@ -103,6 +108,23 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
|
||||
|
||||
i += 1
|
||||
|
||||
_REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
|
||||
|
||||
def resume(self):
|
||||
self._redraw_activity_ring()
|
||||
if self._redraw_id is None:
|
||||
self._redraw_id = gobject.timeout_add(self._REDRAW_TIMEOUT,
|
||||
self._redraw_activity_ring)
|
||||
|
||||
def suspend(self):
|
||||
if self._redraw_id is not None:
|
||||
gobject.source_remove(self._redraw_id)
|
||||
self._redraw_id = None
|
||||
|
||||
def _redraw_activity_ring(self):
|
||||
self._donut.emit_request_changed()
|
||||
return True
|
||||
|
||||
def has_activities(self):
|
||||
return self._donut.has_activities()
|
||||
|
||||
|
@ -64,6 +64,7 @@ class HomeWindow(gtk.Window):
|
||||
self._mesh_box = MeshBox(shell)
|
||||
self._transition_box = TransitionBox()
|
||||
|
||||
self._activate_view()
|
||||
self._canvas.set_root(self._home_box)
|
||||
|
||||
self._transition_box.connect('completed',
|
||||
@ -93,22 +94,28 @@ class HomeWindow(gtk.Window):
|
||||
if keyname == "Alt_L":
|
||||
self._home_box.release()
|
||||
|
||||
def _update_mesh_state(self):
|
||||
if self._active and self._level == ShellModel.ZOOM_MESH:
|
||||
self._mesh_box.resume()
|
||||
else:
|
||||
def _deactivate_view(self):
|
||||
if self._level == ShellModel.ZOOM_HOME:
|
||||
self._home_box.suspend()
|
||||
elif self._level == ShellModel.ZOOM_MESH:
|
||||
self._mesh_box.suspend()
|
||||
|
||||
def _activate_view(self):
|
||||
if self._level == ShellModel.ZOOM_HOME:
|
||||
self._home_box.resume()
|
||||
elif self._level == ShellModel.ZOOM_MESH:
|
||||
self._mesh_box.resume()
|
||||
|
||||
def _focus_in_cb(self, widget, event):
|
||||
self._active = True
|
||||
self._update_mesh_state()
|
||||
self._activate_view()
|
||||
|
||||
def _focus_out_cb(self, widget, event):
|
||||
self._active = False
|
||||
self._update_mesh_state()
|
||||
|
||||
self._deactivate_view()
|
||||
|
||||
def set_zoom_level(self, level):
|
||||
self._deactivate_view()
|
||||
self._level = level
|
||||
self._activate_view()
|
||||
|
||||
self._canvas.set_root(self._transition_box)
|
||||
|
||||
@ -129,7 +136,5 @@ class HomeWindow(gtk.Window):
|
||||
elif self._level == ShellModel.ZOOM_MESH:
|
||||
self._canvas.set_root(self._mesh_box)
|
||||
|
||||
self._update_mesh_state()
|
||||
|
||||
def get_home_box(self):
|
||||
return self._home_box
|
||||
|
@ -36,7 +36,7 @@ from view.BuddyIcon import BuddyIcon
|
||||
from view.pulsingicon import PulsingIcon
|
||||
from view.home.snowflakelayout import SnowflakeLayout
|
||||
|
||||
_ICON_NAME = 'device-network-wireless'
|
||||
_ICON_NAME = 'network-wireless'
|
||||
|
||||
class AccessPointView(PulsingIcon):
|
||||
def __init__(self, model):
|
||||
@ -116,7 +116,7 @@ class AccessPointView(PulsingIcon):
|
||||
]
|
||||
|
||||
|
||||
_MESH_ICON_NAME = 'theme:device-network-mesh'
|
||||
_MESH_ICON_NAME = 'theme:network-mesh'
|
||||
|
||||
class MeshDeviceView(PulsingIcon):
|
||||
def __init__(self, nm_device):
|
||||
|
@ -20,5 +20,5 @@ from sugar import profile
|
||||
class MyIcon(CanvasIcon):
|
||||
def __init__(self, size):
|
||||
CanvasIcon.__init__(self, size=size,
|
||||
icon_name='theme:stock-buddy',
|
||||
icon_name='theme:xo',
|
||||
xo_color=profile.get_color())
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import colorsys
|
||||
from gettext import gettext as _
|
||||
import logging
|
||||
import math
|
||||
|
||||
import hippo
|
||||
@ -317,10 +318,12 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
||||
'expected format' % pid)
|
||||
|
||||
# Next, see how much free memory is left.
|
||||
free_memory = 0
|
||||
try:
|
||||
meminfo = open('/proc/meminfo')
|
||||
meminfo.readline()
|
||||
free_memory = int(meminfo.readline()[9:-3])
|
||||
for line in meminfo.readlines():
|
||||
if line.startswith('MemFree:') or line.startswith('SwapFree:'):
|
||||
free_memory += int(line[9:-3])
|
||||
meminfo.close()
|
||||
except IOError:
|
||||
logging.warn('ActivitiesDonut: could not read /proc/meminfo')
|
||||
|
@ -28,7 +28,7 @@ from sugar._sugaruiext import KeyGrabber
|
||||
|
||||
_BRIGHTNESS_STEP = 2
|
||||
_VOLUME_STEP = 10
|
||||
_BRIGTHNESS_MAX = 15
|
||||
_BRIGHTNESS_MAX = 15
|
||||
_VOLUME_MAX = 100
|
||||
|
||||
_actions_table = {
|
||||
@ -224,4 +224,4 @@ class KeyHandler(object):
|
||||
proxy = bus.get_object('org.laptop.sugar.Console',
|
||||
'/org/laptop/sugar/Console')
|
||||
console = dbus.Interface(proxy, 'org.laptop.sugar.Console')
|
||||
console.toggle_visibility()
|
||||
console.ToggleVisibility()
|
||||
|
@ -96,7 +96,7 @@ def _start_matchbox():
|
||||
cmd = ['matchbox-window-manager']
|
||||
|
||||
cmd.extend(['-use_titlebar', 'no'])
|
||||
cmd.extend(['-theme', 'olpc'])
|
||||
cmd.extend(['-theme', 'sugar'])
|
||||
|
||||
log.debug( 'Matchbox command: %s', " ".join( cmd) )
|
||||
gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
|
||||
@ -148,16 +148,20 @@ def main():
|
||||
os.environ['SUGAR_XO_STYLE'] = 'no'
|
||||
|
||||
os.environ['GTK2_RC_FILES'] = env.get_data_path(gtkrc_filename)
|
||||
print os.environ['GTK2_RC_FILES']
|
||||
|
||||
command = ['dbus-launch', 'dbus-launch', '--exit-with-session']
|
||||
|
||||
if not args:
|
||||
program = 'sugar-shell'
|
||||
command.append('sugar-shell')
|
||||
else:
|
||||
_start_matchbox()
|
||||
program = args[0]
|
||||
|
||||
if args[0].endswith('.py'):
|
||||
command.append('python')
|
||||
|
||||
command.append(args[0])
|
||||
|
||||
command = ['dbus-launch', 'dbus-launch', '--exit-with-session', program]
|
||||
log.info( "Attempting to launch sugar to replace this process: %s", " ".join(command) )
|
||||
log.info( "Attempting to launch sugar to replace this process: %s", " ".join(command))
|
||||
os.execlp( *command )
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -22,8 +22,25 @@
|
||||
(gtype-id "SUGAR_TYPE_MENU")
|
||||
)
|
||||
|
||||
(define-object IconEntry
|
||||
(in-module "Sexy")
|
||||
(parent "GtkEntry")
|
||||
(c-name "SexyIconEntry")
|
||||
(gtype-id "SEXY_TYPE_ICON_ENTRY")
|
||||
)
|
||||
|
||||
;; Enumerations and flags ...
|
||||
|
||||
(define-enum IconEntryPosition
|
||||
(in-module "Sexy")
|
||||
(c-name "SexyIconEntryPosition")
|
||||
(gtype-id "SEXY_TYPE_ICON_ENTRY_POSITION")
|
||||
(values
|
||||
'("primary" "SEXY_ICON_ENTRY_PRIMARY")
|
||||
'("secondary" "SEXY_ICON_ENTRY_SECONDARY")
|
||||
)
|
||||
)
|
||||
|
||||
;; From sugar-menu.h
|
||||
|
||||
(define-method set_active
|
||||
@ -94,3 +111,61 @@
|
||||
'("const-char*" "property")
|
||||
)
|
||||
)
|
||||
|
||||
;; From sexy-icon-entry.h
|
||||
|
||||
(define-function sexy_icon_entry_get_type
|
||||
(c-name "sexy_icon_entry_get_type")
|
||||
(return-type "GType")
|
||||
)
|
||||
|
||||
(define-function sexy_icon_entry_new
|
||||
(c-name "sexy_icon_entry_new")
|
||||
(is-constructor-of "SexyIconEntry")
|
||||
(return-type "GtkWidget*")
|
||||
)
|
||||
|
||||
(define-method set_icon
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_set_icon")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
'("GtkImage*" "icon")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method set_icon_highlight
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_set_icon_highlight")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
'("gboolean" "highlight")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method get_icon
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_get_icon")
|
||||
(return-type "GtkImage*")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method get_icon_highlight
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_get_icon_highlight")
|
||||
(return-type "gboolean")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method add_clear_button
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_add_clear_button")
|
||||
(return-type "none")
|
||||
)
|
||||
|
||||
|
@ -8,6 +8,7 @@ headers
|
||||
#include "sugar-key-grabber.h"
|
||||
#include "sugar-menu.h"
|
||||
#include "sugar-x11-util.h"
|
||||
#include "sexy-icon-entry.h"
|
||||
|
||||
#include <pygtk/pygtk.h>
|
||||
#include <glib.h>
|
||||
@ -20,6 +21,7 @@ import gtk.Entry as PyGtkEntry_Type
|
||||
import gtk.Menu as PyGtkMenu_Type
|
||||
import gtk.Container as PyGtkContainer_Type
|
||||
import gtk.gdk.Window as PyGdkWindow_Type
|
||||
import gtk.Image as PyGtkImage_Type
|
||||
%%
|
||||
ignore-glob
|
||||
*_get_type
|
||||
|
@ -27,6 +27,7 @@
|
||||
extern PyMethodDef py_sugaruiext_functions[];
|
||||
|
||||
void py_sugaruiext_register_classes (PyObject *d);
|
||||
void py_sugaruiext_add_constants (PyObject *module, const gchar *strip_prefix);
|
||||
|
||||
DL_EXPORT(void)
|
||||
init_sugaruiext(void)
|
||||
@ -39,6 +40,7 @@ init_sugaruiext(void)
|
||||
d = PyModule_GetDict (m);
|
||||
|
||||
py_sugaruiext_register_classes (d);
|
||||
py_sugaruiext_add_constants(m, "SEXY_");
|
||||
|
||||
if (PyErr_Occurred ()) {
|
||||
Py_FatalError ("can't initialise module _sugaruiext");
|
||||
|
@ -52,6 +52,8 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
self._activity = activity
|
||||
activity.connect('shared', self._activity_shared_cb)
|
||||
activity.connect('joined', self._activity_shared_cb)
|
||||
activity.connect('notify::max_participants',
|
||||
self._max_participants_changed_cb)
|
||||
|
||||
if activity.metadata:
|
||||
self.title = gtk.Entry()
|
||||
@ -74,11 +76,11 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
'theme:zoom-home-mini')
|
||||
self.share.combo.append_item(None, _('My Neighborhood'),
|
||||
'theme:zoom-neighborhood-mini')
|
||||
self._update_share()
|
||||
|
||||
self.insert(self.share, -1)
|
||||
self.share.show()
|
||||
|
||||
self._update_share()
|
||||
|
||||
self.keep = ToolButton('document-save')
|
||||
self.keep.set_tooltip(_('Keep'))
|
||||
self.keep.connect('clicked', self._keep_clicked_cb)
|
||||
@ -94,6 +96,9 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
self._update_title_sid = None
|
||||
|
||||
def _update_share(self):
|
||||
if self._activity.props.max_participants == 1:
|
||||
self.share.hide()
|
||||
|
||||
if self._activity.get_shared():
|
||||
self.share.set_sensitive(False)
|
||||
self.share.combo.set_active(self.SHARE_NEIGHBORHOOD)
|
||||
@ -139,6 +144,9 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
def _activity_shared_cb(self, activity):
|
||||
self._update_share()
|
||||
|
||||
def _max_participants_changed_cb(self, activity, pspec):
|
||||
self._update_share()
|
||||
|
||||
class EditToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
@ -185,7 +193,10 @@ class Activity(Window, gtk.Container):
|
||||
}
|
||||
|
||||
__gproperties__ = {
|
||||
'active': (bool, None, None, False, gobject.PARAM_READWRITE)
|
||||
'active' : (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE),
|
||||
'max-participants': (int, None, None, 0, 1000, 0,
|
||||
gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
def __init__(self, handle, create_jobject=True):
|
||||
@ -235,6 +246,7 @@ class Activity(Window, gtk.Container):
|
||||
self._preview = None
|
||||
self._updating_jobject = False
|
||||
self._closing = False
|
||||
self._max_participants = 0
|
||||
|
||||
shared_activity = handle.get_shared_activity()
|
||||
if shared_activity:
|
||||
@ -280,10 +292,14 @@ class Activity(Window, gtk.Container):
|
||||
self._active = value
|
||||
if not self._active and self._jobject:
|
||||
self.save()
|
||||
elif pspec.name == 'max-participants':
|
||||
self._max_participants = value
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'active':
|
||||
return self._active
|
||||
elif pspec.name == 'max-participants':
|
||||
return self._max_participants
|
||||
|
||||
def get_id(self):
|
||||
return self._activity_id
|
||||
|
@ -56,6 +56,6 @@ class ActivityService(dbus.service.Object):
|
||||
self._activity = activity
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE)
|
||||
def set_active(self, active):
|
||||
def SetActive(self, active):
|
||||
logging.debug('ActivityService.set_active: %s.' % active)
|
||||
self._activity.props.active = active
|
||||
|
@ -21,6 +21,7 @@ import zipfile
|
||||
import shutil
|
||||
import subprocess
|
||||
import re
|
||||
import gettext
|
||||
|
||||
from sugar import env
|
||||
from sugar.activity.bundle import Bundle
|
||||
@ -117,7 +118,7 @@ setup.py dist - create a bundle package \n\
|
||||
setup.py install [dirname] - install the bundle \n\
|
||||
setup.py uninstall [dirname] - uninstall the bundle \n\
|
||||
setup.py genpot - generate the gettext pot file \n\
|
||||
setup.py genmo - compile gettext po files in mo \n\
|
||||
setup.py genl10n - generate localization files \n\
|
||||
setup.py clean - clean the directory \n\
|
||||
setup.py release - do a new release of the bundle \n\
|
||||
setup.py help - print this message \n\
|
||||
@ -157,17 +158,26 @@ def _get_po_list(manifest):
|
||||
|
||||
return file_list
|
||||
|
||||
def _get_mo_list(manifest):
|
||||
mo_list = []
|
||||
def _get_l10n_list(manifest):
|
||||
l10n_list = []
|
||||
|
||||
for lang in _get_po_list(manifest).keys():
|
||||
filename = _get_service_name() + '.mo'
|
||||
mo_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename))
|
||||
l10n_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename))
|
||||
l10n_list.append(os.path.join('locale', lang, 'activity.linfo'))
|
||||
|
||||
return mo_list
|
||||
return l10n_list
|
||||
|
||||
def _get_activity_name():
|
||||
info_path = os.path.join(_get_source_path(), 'activity', 'activity.info')
|
||||
f = open(info_path,'r')
|
||||
info = f.read()
|
||||
f.close()
|
||||
match = re.search('^name\s*=\s*(.*)$', info, flags = re.MULTILINE)
|
||||
return match.group(1)
|
||||
|
||||
def cmd_dist(bundle_name, manifest):
|
||||
cmd_genmo(bundle_name, manifest)
|
||||
cmd_genl10n(bundle_name, manifest)
|
||||
file_list = _get_file_list(manifest)
|
||||
|
||||
zipname = _get_package_name(bundle_name)
|
||||
@ -177,7 +187,7 @@ def cmd_dist(bundle_name, manifest):
|
||||
for filename in file_list:
|
||||
bundle_zip.write(filename, os.path.join(base_dir, filename))
|
||||
|
||||
for filename in _get_mo_list(manifest):
|
||||
for filename in _get_l10n_list(manifest):
|
||||
bundle_zip.write(filename, os.path.join(base_dir, filename))
|
||||
|
||||
bundle_zip.close()
|
||||
@ -205,8 +215,21 @@ def cmd_genpot(bundle_name, manifest):
|
||||
if file_name.endswith('.py'):
|
||||
python_files.append(file_name)
|
||||
|
||||
# First write out a stub .pot file containing just the translated
|
||||
# activity name, then have xgettext merge the rest of the
|
||||
# translations into that. (We can't just append the activity name
|
||||
# to the end of the .pot file afterwards, because that might
|
||||
# create a duplicate msgid.)
|
||||
pot_file = os.path.join('po', '%s.pot' % bundle_name)
|
||||
args = [ 'xgettext', '--language=Python',
|
||||
activity_name = _get_activity_name()
|
||||
escaped_name = re.sub('([\\\\"])', '\\\\\\1', activity_name)
|
||||
f = open(pot_file, 'w')
|
||||
f.write('#: activity/activity.info:2\n')
|
||||
f.write('msgid "%s"\n' % escaped_name)
|
||||
f.write('msgstr ""\n')
|
||||
f.close()
|
||||
|
||||
args = [ 'xgettext', '--join-existing', '--language=Python',
|
||||
'--keyword=_', '--output=%s' % pot_file ]
|
||||
|
||||
args += python_files
|
||||
@ -220,14 +243,16 @@ def cmd_genpot(bundle_name, manifest):
|
||||
if retcode:
|
||||
print 'ERROR - msgmerge failed with return code %i.' % retcode
|
||||
|
||||
def cmd_genmo(bundle_name, manifest):
|
||||
def cmd_genl10n(bundle_name, manifest):
|
||||
source_path = _get_source_path()
|
||||
activity_name = _get_activity_name()
|
||||
|
||||
po_list = _get_po_list(manifest)
|
||||
for lang in po_list.keys():
|
||||
file_name = po_list[lang]
|
||||
|
||||
mo_path = os.path.join(source_path, 'locale', lang, 'LC_MESSAGES')
|
||||
localedir = os.path.join(source_path, 'locale', lang)
|
||||
mo_path = os.path.join(localedir, 'LC_MESSAGES')
|
||||
if not os.path.isdir(mo_path):
|
||||
os.makedirs(mo_path)
|
||||
|
||||
@ -237,6 +262,13 @@ def cmd_genmo(bundle_name, manifest):
|
||||
if retcode:
|
||||
print 'ERROR - msgfmt failed with return code %i.' % retcode
|
||||
|
||||
cat = gettext.GNUTranslations(open(mo_file, 'r'))
|
||||
translated_name = cat.gettext(activity_name)
|
||||
linfo_file = os.path.join(localedir, 'activity.linfo')
|
||||
f = open(linfo_file, 'w')
|
||||
f.write('[Activity]\nname = %s\n' % translated_name)
|
||||
f.close()
|
||||
|
||||
def cmd_release(bundle_name, manifest):
|
||||
if not os.path.isdir('.git'):
|
||||
print 'ERROR - this command works only for git repositories'
|
||||
@ -358,8 +390,8 @@ def start(bundle_name, manifest='MANIFEST'):
|
||||
cmd_uninstall(sys.argv[2])
|
||||
elif sys.argv[1] == 'genpot':
|
||||
cmd_genpot(bundle_name, manifest)
|
||||
elif sys.argv[1] == 'genmo':
|
||||
cmd_genmo(bundle_name, manifest)
|
||||
elif sys.argv[1] == 'genl10n':
|
||||
cmd_genl10n(bundle_name, manifest)
|
||||
elif sys.argv[1] == 'clean':
|
||||
cmd_clean()
|
||||
elif sys.argv[1] == 'release':
|
||||
|
@ -23,7 +23,7 @@ NAME_KEY = 'NAME'
|
||||
PERCENT_KEY = 'PERCENT'
|
||||
ICON_KEY = 'ICON'
|
||||
PREVIEW_KEY = 'PREVIEW'
|
||||
ACTIVITY_KEY = 'ACTIVITY'
|
||||
ACTIVITIES_KEY = 'ACTIVITIES'
|
||||
FORMATS_KEY = 'FORMATS'
|
||||
|
||||
TYPE_KEY = 'TYPE'
|
||||
@ -51,7 +51,7 @@ class ClipboardService(gobject.GObject):
|
||||
'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
([str])),
|
||||
'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
([str, str, int, str, str, str])),
|
||||
([str, str, int, str, str, object])),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@ -118,13 +118,13 @@ class ClipboardService(gobject.GObject):
|
||||
percent
|
||||
icon
|
||||
preview
|
||||
activity
|
||||
activities
|
||||
|
||||
From the ClipboardObject instance which is being described.
|
||||
"""
|
||||
self.emit('object-state-changed', str(object_id), values[NAME_KEY],
|
||||
values[PERCENT_KEY], values[ICON_KEY], values[PREVIEW_KEY],
|
||||
values[ACTIVITY_KEY])
|
||||
values[ACTIVITIES_KEY])
|
||||
|
||||
def add_object(self, name):
|
||||
"""Add a new object to the path
|
||||
@ -193,7 +193,7 @@ class ClipboardService(gobject.GObject):
|
||||
PERCENT_KEY: number,
|
||||
ICON_KEY: str,
|
||||
PREVIEW_KEY: XXX what is it?,
|
||||
ACTIVITY_KEY: source activity id,
|
||||
ACTIVITIES_KEY: activities that can open this object,
|
||||
FORMATS_KEY: list of XXX what is it?
|
||||
"""
|
||||
return self._dbus_service.get_object(dbus.ObjectPath(object_id),)
|
||||
|
@ -120,6 +120,9 @@ class DSObject(object):
|
||||
|
||||
def resume(self, service_name=None):
|
||||
if self.is_bundle():
|
||||
if service_name is not None:
|
||||
raise ValueError('Object is a bundle, cannot be resumed as an activity.')
|
||||
|
||||
bundle = Bundle(self.file_path)
|
||||
if not bundle.is_installed():
|
||||
bundle.install()
|
||||
|
@ -28,55 +28,63 @@ DS_DBUS_SERVICE = "org.laptop.sugar.DataStore"
|
||||
DS_DBUS_INTERFACE = "org.laptop.sugar.DataStore"
|
||||
DS_DBUS_PATH = "/org/laptop/sugar/DataStore"
|
||||
|
||||
_bus = dbus.SessionBus()
|
||||
_data_store = dbus.Interface(_bus.get_object(DS_DBUS_SERVICE, DS_DBUS_PATH),
|
||||
DS_DBUS_INTERFACE)
|
||||
_data_store = None
|
||||
|
||||
def _get_data_store():
|
||||
global _data_store
|
||||
|
||||
if not _data_store:
|
||||
_bus = dbus.SessionBus()
|
||||
_data_store = dbus.Interface(_bus.get_object(DS_DBUS_SERVICE,
|
||||
DS_DBUS_PATH),
|
||||
DS_DBUS_INTERFACE)
|
||||
return _data_store
|
||||
|
||||
def create(properties, filename):
|
||||
object_id = _data_store.create(dbus.Dictionary(properties), filename)
|
||||
object_id = _get_data_store().create(dbus.Dictionary(properties), filename)
|
||||
logging.debug('dbus_helpers.create: ' + object_id)
|
||||
return object_id
|
||||
|
||||
def update(uid, properties, filename, reply_handler=None, error_handler=None, timeout=-1):
|
||||
logging.debug('dbus_helpers.update: %s, %s, %s' % (uid, filename, properties))
|
||||
if reply_handler and error_handler:
|
||||
_data_store.update(uid, dbus.Dictionary(properties), filename,
|
||||
_get_data_store().update(uid, dbus.Dictionary(properties), filename,
|
||||
reply_handler=reply_handler,
|
||||
error_handler=error_handler,
|
||||
timeout=timeout)
|
||||
else:
|
||||
_data_store.update(uid, dbus.Dictionary(properties), filename)
|
||||
_get_data_store().update(uid, dbus.Dictionary(properties), filename)
|
||||
|
||||
def delete(uid):
|
||||
logging.debug('dbus_helpers.delete: %r' % uid)
|
||||
_data_store.delete(uid)
|
||||
_get_data_store().delete(uid)
|
||||
|
||||
def get_properties(uid):
|
||||
logging.debug('dbus_helpers.get_properties: %s' % uid)
|
||||
return _data_store.get_properties(uid)
|
||||
return _get_data_store().get_properties(uid)
|
||||
|
||||
def get_filename(uid):
|
||||
filename = _data_store.get_filename(uid)
|
||||
filename = _get_data_store().get_filename(uid)
|
||||
logging.debug('dbus_helpers.get_filename: %s, %s' % (uid, filename))
|
||||
return filename
|
||||
|
||||
def find(query, reply_handler, error_handler):
|
||||
logging.debug('dbus_helpers.find: %r' % query)
|
||||
if reply_handler and error_handler:
|
||||
return _data_store.find(query, reply_handler=reply_handler,
|
||||
return _get_data_store().find(query, reply_handler=reply_handler,
|
||||
error_handler=error_handler)
|
||||
else:
|
||||
return _data_store.find(query)
|
||||
return _get_data_store().find(query)
|
||||
|
||||
def mount(uri, options):
|
||||
return _data_store.mount(uri, options)
|
||||
return _get_data_store().mount(uri, options)
|
||||
|
||||
def unmount(mount_point_id):
|
||||
_data_store.unmount(mount_point_id)
|
||||
_get_data_store().unmount(mount_point_id)
|
||||
|
||||
def mounts():
|
||||
return _data_store.mounts()
|
||||
return _get_data_store().mounts()
|
||||
|
||||
def get_unique_values(key):
|
||||
return _data_store.get_uniquevaluesfor(key, dbus.Dictionary({}, signature='ss'))
|
||||
return _get_data_store().get_uniquevaluesfor(key, dbus.Dictionary({}, signature='ss'))
|
||||
|
||||
|
@ -21,12 +21,13 @@ import datetime
|
||||
|
||||
class Date(object):
|
||||
"""Date-object storing a simple time.time() float
|
||||
|
||||
XXX not sure about the rationale for this class,
|
||||
possibly it makes transfer over dbus easier?
|
||||
|
||||
Useful to display dates in the UI in an
|
||||
abbreviated and easy to read format.
|
||||
"""
|
||||
def __init__(self, timestamp):
|
||||
"""Initialise via a timestamp (floating point value)"""
|
||||
self._today = datetime.date.today()
|
||||
self._timestamp = timestamp
|
||||
|
||||
def __str__(self):
|
||||
@ -39,14 +40,13 @@ class Date(object):
|
||||
the year in the date.
|
||||
"""
|
||||
date = datetime.date.fromtimestamp(self._timestamp)
|
||||
today = datetime.date.today()
|
||||
|
||||
# FIXME localization
|
||||
if date == today:
|
||||
if date == self._today:
|
||||
result = 'Today'
|
||||
elif date == today - datetime.timedelta(1):
|
||||
elif date == self._today - datetime.timedelta(1):
|
||||
result = 'Yesterday'
|
||||
elif date.year == today.year:
|
||||
elif date.year == self._today.year:
|
||||
result = date.strftime('%B %d')
|
||||
else:
|
||||
result = date.strftime('%B %d, %Y')
|
||||
|
@ -9,7 +9,7 @@ sugar_PYTHON = \
|
||||
combobox.py \
|
||||
icon.py \
|
||||
iconbutton.py \
|
||||
menuitem.py \
|
||||
iconentry.py \
|
||||
notebook.py \
|
||||
objectchooser.py \
|
||||
radiotoolbutton.py \
|
||||
|
@ -25,7 +25,7 @@ class CanvasButton(hippo.CanvasButton):
|
||||
hippo.CanvasButton.__init__(self, text=label)
|
||||
|
||||
if icon_name:
|
||||
icon = Icon(icon_name, gtk.ICON_SIZE_BUTTON)
|
||||
icon = Icon(icon_name,icon_size=gtk.ICON_SIZE_BUTTON)
|
||||
self.props.widget.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
|
@ -142,6 +142,8 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
gobject.PARAM_READWRITE),
|
||||
'size' : (int, None, None, 0, 1024, 0,
|
||||
gobject.PARAM_READWRITE),
|
||||
'scale' : (int, None, None, 0, 1024, 0,
|
||||
gobject.PARAM_READWRITE),
|
||||
'cache' : (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE),
|
||||
'active' : (bool, None, None, True,
|
||||
@ -156,6 +158,7 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
self._buffers = {}
|
||||
self._cur_buffer = None
|
||||
self._size = 0
|
||||
self._scale = 0
|
||||
self._fill_color = None
|
||||
self._stroke_color = None
|
||||
self._icon_name = None
|
||||
@ -210,6 +213,11 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
self._clear_buffers()
|
||||
self._size = value
|
||||
self.emit_request_changed()
|
||||
elif pspec.name == 'scale':
|
||||
if self._scale != value and not self._cache:
|
||||
self._clear_buffers()
|
||||
self._scale = value
|
||||
self.emit_request_changed()
|
||||
elif pspec.name == 'cache':
|
||||
self._cache = value
|
||||
elif pspec.name == 'active':
|
||||
@ -277,6 +285,8 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
return self._active
|
||||
elif pspec.name == 'badge-name':
|
||||
return self._badge_name
|
||||
elif pspec.name == 'scale':
|
||||
return self._scale
|
||||
|
||||
def _get_icon_size(self, handle):
|
||||
if handle:
|
||||
@ -286,9 +296,11 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
return [0, 0]
|
||||
|
||||
def _get_size(self, handle):
|
||||
if self._size == 0:
|
||||
width, height = self._get_icon_size(handle)
|
||||
else:
|
||||
width, height = self._get_icon_size(handle)
|
||||
if self._scale != 0:
|
||||
width = int(width * self._scale)
|
||||
height = int(height * self._scale)
|
||||
elif self._size != 0:
|
||||
width = height = self._size
|
||||
|
||||
return [width, height]
|
||||
|
@ -34,17 +34,18 @@ class Icon(gtk.Image):
|
||||
gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
def __init__(self, name, size=gtk.ICON_SIZE_LARGE_TOOLBAR, **kwargs):
|
||||
def __init__(self, name, **kwargs):
|
||||
self._constructed = False
|
||||
self._fill_color = None
|
||||
self._stroke_color = None
|
||||
self._icon_name = name
|
||||
self._size = size
|
||||
self._theme = gtk.icon_theme_get_default()
|
||||
self._data = None
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
# If we have a non-styled-icon
|
||||
if not self._fill_color and not self._stroke_color:
|
||||
self._update_normal_icon()
|
||||
self._constructed = True
|
||||
self._update_icon()
|
||||
|
||||
def _get_pixbuf(self, data, width, height):
|
||||
loader = gtk.gdk.PixbufLoader('svg')
|
||||
@ -77,9 +78,12 @@ class Icon(gtk.Image):
|
||||
source.set_state(gtk.STATE_INSENSITIVE)
|
||||
icon_set.add_source(source)
|
||||
|
||||
self.set_from_icon_set(icon_set, self._size)
|
||||
self.props.icon_set = icon_set
|
||||
|
||||
def _update_icon(self):
|
||||
if not self._constructed:
|
||||
return
|
||||
|
||||
if not self._fill_color and not self._stroke_color:
|
||||
self._update_normal_icon()
|
||||
return
|
||||
@ -100,12 +104,12 @@ class Icon(gtk.Image):
|
||||
self._data = data
|
||||
|
||||
# Redraw pixbuf
|
||||
[w, h] = gtk.icon_size_lookup(self._size)
|
||||
[w, h] = gtk.icon_size_lookup(self.props.icon_size)
|
||||
pixbuf = self._get_pixbuf(self._data, w, h)
|
||||
self.set_from_pixbuf(pixbuf)
|
||||
|
||||
def _get_real_name(self, name):
|
||||
info = self._theme.lookup_icon(name, self._size, 0)
|
||||
info = self._theme.lookup_icon(name, self.props.icon_size, 0)
|
||||
if not info:
|
||||
raise ValueError("Icon '" + name + "' not found.")
|
||||
fname = info.get_filename()
|
||||
@ -122,9 +126,16 @@ class Icon(gtk.Image):
|
||||
elif pspec.name == 'stroke-color':
|
||||
self._stroke_color = value
|
||||
self._update_icon()
|
||||
else:
|
||||
gtk.Image.do_set_property(self, pspec, value)
|
||||
|
||||
if pspec.name == 'icon-size':
|
||||
self._update_icon()
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'fill-color':
|
||||
return self._fill_color
|
||||
elif pspec.name == 'stroke-color':
|
||||
return self._stroke_color
|
||||
else:
|
||||
return gtk.Image.do_get_property(self, pspec)
|
||||
|
45
sugar/graphics/iconentry.py
Normal file
45
sugar/graphics/iconentry.py
Normal file
@ -0,0 +1,45 @@
|
||||
# Copyright (C) 2007, One Laptop Per Child
|
||||
#
|
||||
# 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 gtk
|
||||
|
||||
from sugar import _sugaruiext
|
||||
|
||||
ICON_ENTRY_PRIMARY = _sugaruiext.ICON_ENTRY_PRIMARY
|
||||
ICON_ENTRY_SECONDARY = _sugaruiext.ICON_ENTRY_SECONDARY
|
||||
|
||||
class IconEntry(_sugaruiext.IconEntry):
|
||||
def set_icon_from_name(self, position, name):
|
||||
icon_theme = gtk.icon_theme_get_default()
|
||||
icon_info = icon_theme.lookup_icon(name,
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR,
|
||||
0)
|
||||
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.get_filename())
|
||||
|
||||
image = gtk.Image()
|
||||
image.set_from_pixbuf(pixbuf)
|
||||
image.show()
|
||||
|
||||
self.set_icon(position, image)
|
||||
|
||||
def set_icon(self, position, image):
|
||||
if image.get_storage_type() not in [gtk.IMAGE_PIXBUF, gtk.IMAGE_STOCK]:
|
||||
raise ValueError('Image must have a storage type of pixbuf or ' +
|
||||
'stock, not %r.' % image.get_storage_type())
|
||||
_sugaruiext.IconEntry.set_icon(self, position, image)
|
||||
|
@ -22,7 +22,7 @@ class MenuItem(gtk.ImageMenuItem):
|
||||
def __init__(self, text_label, icon_name=None):
|
||||
gtk.ImageMenuItem.__init__(self, text_label)
|
||||
if icon_name:
|
||||
icon = Icon(icon_name, gtk.ICON_SIZE_MENU)
|
||||
icon = Icon(icon_name, icon_size=gtk.ICON_SIZE_MENU)
|
||||
self.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
|
@ -150,7 +150,7 @@ class CollapsedEntry(CanvasRoundBox):
|
||||
self._icon_name = type.icon
|
||||
|
||||
if not self._icon_name:
|
||||
self._icon_name = 'theme:stock-missing'
|
||||
self._icon_name = 'theme:image-missing'
|
||||
|
||||
return self._icon_name
|
||||
|
||||
|
@ -87,9 +87,7 @@ class Palette(gtk.Window):
|
||||
'invoker' : (object, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'position' : (gobject.TYPE_INT, None, None, 0, 6,
|
||||
0, gobject.PARAM_READWRITE),
|
||||
'draw-gap' : (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE)
|
||||
0, gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
__gsignals__ = {
|
||||
@ -114,7 +112,6 @@ class Palette(gtk.Window):
|
||||
self._group_id = None
|
||||
self._up = False
|
||||
self._position = self.DEFAULT
|
||||
self._draw_gap = False
|
||||
self._palette_popup_sid = None
|
||||
|
||||
self._popup_anim = animator.Animator(0.3, 10)
|
||||
@ -162,6 +159,7 @@ class Palette(gtk.Window):
|
||||
self._leave_notify_event_cb)
|
||||
|
||||
self.set_primary_text(label, accel_path)
|
||||
self.set_group_id('default')
|
||||
|
||||
def is_up(self):
|
||||
return self._up
|
||||
@ -178,8 +176,9 @@ class Palette(gtk.Window):
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
|
||||
def set_primary_text(self, label, accel_path=None):
|
||||
self._label.set_text(label)
|
||||
self._label.show()
|
||||
if label is not None:
|
||||
self._label.set_text(label)
|
||||
self._label.show()
|
||||
|
||||
def set_content(self, widget):
|
||||
if len(self._content.get_children()) > 0:
|
||||
@ -196,6 +195,7 @@ class Palette(gtk.Window):
|
||||
group = palettegroup.get_group(self._group_id)
|
||||
group.remove(self)
|
||||
if group_id:
|
||||
self._group_id = group_id
|
||||
group = palettegroup.get_group(group_id)
|
||||
group.add(self)
|
||||
|
||||
@ -206,9 +206,6 @@ class Palette(gtk.Window):
|
||||
self._invoker.connect('mouse-leave', self._invoker_mouse_leave_cb)
|
||||
elif pspec.name == 'position':
|
||||
self._position = value
|
||||
elif pspec.name == 'draw-gap':
|
||||
self._draw_gap = value
|
||||
self.queue_draw()
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
@ -217,8 +214,6 @@ class Palette(gtk.Window):
|
||||
return self._invoker
|
||||
elif pspec.name == 'position':
|
||||
return self._position
|
||||
elif pspec.name == 'draw-gap':
|
||||
return self._draw_gap
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
@ -228,7 +223,7 @@ class Palette(gtk.Window):
|
||||
|
||||
def do_expose_event(self, event):
|
||||
# We want to draw a border with a beautiful gap
|
||||
if self._draw_gap:
|
||||
if self._invoker.has_rectangle_gap():
|
||||
invoker = self._invoker.get_rect()
|
||||
palette = self.get_rect()
|
||||
|
||||
@ -398,6 +393,9 @@ class Palette(gtk.Window):
|
||||
self.menu.set_active(True)
|
||||
self.show()
|
||||
|
||||
if self._invoker:
|
||||
self._invoker.notify_popup()
|
||||
|
||||
self._up = True
|
||||
_palette_observer.emit('popup', self)
|
||||
self.emit('popup')
|
||||
@ -406,22 +404,31 @@ class Palette(gtk.Window):
|
||||
if not self._palette_popup_sid is None:
|
||||
_palette_observer.disconnect(self._palette_popup_sid)
|
||||
self._palette_popup_sid = None
|
||||
|
||||
self.menu.set_active(False)
|
||||
self.hide()
|
||||
|
||||
if self._invoker:
|
||||
self._invoker.notify_popdown()
|
||||
|
||||
self._up = False
|
||||
self.emit('popdown')
|
||||
|
||||
def popup(self):
|
||||
def popup(self, immediate=False):
|
||||
self._popdown_anim.stop()
|
||||
self._popup_anim.start()
|
||||
|
||||
if not immediate:
|
||||
self._popup_anim.start()
|
||||
else:
|
||||
self._show()
|
||||
|
||||
self._secondary_anim.start()
|
||||
|
||||
def popdown(self, inmediate=False):
|
||||
def popdown(self, immediate=False):
|
||||
self._secondary_anim.stop()
|
||||
self._popup_anim.stop()
|
||||
|
||||
if not inmediate:
|
||||
if not immediate:
|
||||
self._popdown_anim.start()
|
||||
else:
|
||||
self._hide()
|
||||
@ -440,7 +447,15 @@ class Palette(gtk.Window):
|
||||
self._state = state
|
||||
|
||||
def _invoker_mouse_enter_cb(self, invoker):
|
||||
self.popup()
|
||||
immediate = False
|
||||
if self._group_id:
|
||||
group = palettegroup.get_group(self._group_id)
|
||||
if group and group.is_up():
|
||||
immediate = True
|
||||
group.popdown()
|
||||
|
||||
print immediate
|
||||
self.popup(immediate=immediate)
|
||||
|
||||
def _invoker_mouse_leave_cb(self, invoker):
|
||||
self.popdown()
|
||||
@ -535,6 +550,12 @@ class Invoker(gobject.GObject):
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
def has_rectangle_gap(self):
|
||||
return False
|
||||
|
||||
def draw_rectangle(self, event, palette):
|
||||
pass
|
||||
|
||||
def get_default_position(self):
|
||||
return Palette.AROUND
|
||||
|
||||
@ -543,6 +564,12 @@ class Invoker(gobject.GObject):
|
||||
height = gtk.gdk.screen_height()
|
||||
return gtk.gdk.Rectangle(0, 0, width, height)
|
||||
|
||||
def notify_popup(self):
|
||||
pass
|
||||
|
||||
def notify_popdown(self):
|
||||
pass
|
||||
|
||||
class WidgetInvoker(Invoker):
|
||||
def __init__(self, widget):
|
||||
Invoker.__init__(self)
|
||||
@ -562,31 +589,24 @@ class WidgetInvoker(Invoker):
|
||||
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
|
||||
def draw_invoker_rect(self, event, palette):
|
||||
style = self._widget.style
|
||||
if palette.is_up():
|
||||
gap = _calculate_gap(self.get_rect(), palette.get_rect())
|
||||
def has_rectangle_gap(self):
|
||||
return True
|
||||
|
||||
if gap:
|
||||
style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_IN, event.area, self._widget,
|
||||
"palette-invoker",
|
||||
self._widget.allocation.x,
|
||||
self._widget.allocation.y,
|
||||
self._widget.allocation.width,
|
||||
self._widget.allocation.height,
|
||||
gap[0], gap[1], gap[2])
|
||||
else:
|
||||
style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
def draw_rectangle(self, event, palette):
|
||||
style = self._widget.style
|
||||
gap = _calculate_gap(self.get_rect(), palette.get_rect())
|
||||
if gap:
|
||||
style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_IN, event.area, self._widget,
|
||||
"palette-invoker",
|
||||
self._widget.allocation.x,
|
||||
self._widget.allocation.y,
|
||||
self._widget.allocation.width,
|
||||
self._widget.allocation.height)
|
||||
self._widget.allocation.height,
|
||||
gap[0], gap[1], gap[2])
|
||||
else:
|
||||
style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area, self._widget,
|
||||
gtk.SHADOW_IN, event.area, self._widget,
|
||||
"palette-invoker",
|
||||
self._widget.allocation.x,
|
||||
self._widget.allocation.y,
|
||||
@ -602,6 +622,12 @@ class WidgetInvoker(Invoker):
|
||||
def get_toplevel(self):
|
||||
return self._widget.get_toplevel()
|
||||
|
||||
def notify_popup(self):
|
||||
self._widget.queue_draw()
|
||||
|
||||
def notify_popdown(self):
|
||||
self._widget.queue_draw()
|
||||
|
||||
class CanvasInvoker(Invoker):
|
||||
def __init__(self, item):
|
||||
Invoker.__init__(self)
|
||||
|
@ -39,6 +39,7 @@ class Group(gobject.GObject):
|
||||
gobject.GObject.__init__(self)
|
||||
self._up = False
|
||||
self._palettes = []
|
||||
self._sig_ids = {}
|
||||
|
||||
def is_up(self):
|
||||
return self._up
|
||||
@ -46,18 +47,26 @@ class Group(gobject.GObject):
|
||||
def add(self, palette):
|
||||
self._palettes.append(palette)
|
||||
|
||||
self._sig_ids[palette] = []
|
||||
|
||||
sid = palette.connect('popup', self._palette_popup_cb)
|
||||
palette.popup_sid = sid
|
||||
self._sig_ids[palette].append(sid)
|
||||
|
||||
sid = palette.connect('popdown', self._palette_popdown_cb)
|
||||
palette.podown_sid = sid
|
||||
self._sig_ids[palette].append(sid)
|
||||
|
||||
def remove(self, palette):
|
||||
self.disconnect(palette.popup_sid)
|
||||
self.disconnect(palette.popdown_sid)
|
||||
sig_ids = self._sig_ids[palette]
|
||||
for sid in sig_ids:
|
||||
palette.disconnect(sid)
|
||||
|
||||
self._palettes.remove(palette)
|
||||
|
||||
def popdown(self):
|
||||
for palette in self._palettes:
|
||||
if palette.is_up():
|
||||
palette.popdown(immediate=True)
|
||||
|
||||
def _palette_popup_cb(self, palette):
|
||||
if not self._up:
|
||||
self.emit('popup')
|
||||
|
@ -40,25 +40,15 @@ class RadioToolButton(gtk.RadioToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self._palette = Palette(text)
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.props.draw_gap:
|
||||
if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_invoker_rect(event, self._palette)
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
|
||||
gtk.RadioToolButton.do_expose_event(self, event)
|
||||
|
||||
def _palette_changed(self, palette):
|
||||
# Force a redraw to update the invoker rectangle
|
||||
self.queue_draw()
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
@ -128,6 +128,8 @@ COLOR_WHITE = Color('#FFFFFF')
|
||||
COLOR_TRANSPARENT = Color('#FFFFFF', alpha=0.0)
|
||||
COLOR_PANEL_GREY = Color('#C0C0C0')
|
||||
COLOR_SELECTION_GREY = Color('#A6A6A6')
|
||||
COLOR_TOOLBAR_GREY = Color('#404040')
|
||||
COLOR_BUTTON_GREY = Color('#808080')
|
||||
COLOR_INACTIVE_FILL = Color('#9D9FA1')
|
||||
COLOR_INACTIVE_STROKE = Color('#757575')
|
||||
|
||||
|
@ -39,25 +39,15 @@ class ToggleToolButton(gtk.ToggleToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self._palette = Palette(text)
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.props.draw_gap:
|
||||
if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_invoker_rect(event, self._palette)
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
|
||||
gtk.ToggleToolButton.do_expose_event(self, event)
|
||||
|
||||
def _palette_changed(self, palette):
|
||||
# Force a redraw to update the invoker rectangle
|
||||
self.queue_draw()
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
@ -28,7 +28,8 @@ class ToolButton(gtk.ToolButton):
|
||||
def __init__(self, icon_name=None):
|
||||
gtk.ToolButton.__init__(self)
|
||||
self._palette = None
|
||||
self.set_icon(icon_name)
|
||||
if icon_name:
|
||||
self.set_icon(icon_name)
|
||||
self.connect('clicked', self._button_clicked_cb)
|
||||
|
||||
def set_icon(self, icon_name):
|
||||
@ -42,19 +43,14 @@ class ToolButton(gtk.ToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.props.draw_gap:
|
||||
if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_invoker_rect(event, self._palette)
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
|
||||
gtk.ToolButton.do_expose_event(self, event)
|
||||
|
||||
@ -62,8 +58,4 @@ class ToolButton(gtk.ToolButton):
|
||||
if self._palette:
|
||||
self._palette.popdown(True)
|
||||
|
||||
def _palette_changed(self, palette):
|
||||
# Force a redraw to update the invoker rectangle
|
||||
self.queue_draw()
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
@ -105,11 +105,7 @@ def _get_logs_dir():
|
||||
def start(module_id):
|
||||
# Only log if logging is set up for the activity
|
||||
module_key = module_id.upper() + "_DEBUG"
|
||||
emulator = False
|
||||
if os.environ.has_key("SUGAR_EMULATOR"):
|
||||
if os.environ["SUGAR_EMULATOR"] == "yes":
|
||||
emulator = True
|
||||
if not os.environ.has_key(module_key) and not emulator:
|
||||
if not os.environ.has_key(module_key) and not env.is_emulator():
|
||||
return
|
||||
|
||||
log_writer = LogWriter(module_id)
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2006-2007, Red Hat, Inc.
|
||||
# Copyright (C) 2007, One Laptop Per Child
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
@ -20,7 +21,13 @@ import logging
|
||||
from sugar import _sugarext
|
||||
|
||||
def get_for_file(file_name):
|
||||
return _sugarext.get_mime_type_for_file(file_name)
|
||||
mime_type = _sugarext.get_mime_type_for_file(file_name)
|
||||
if mime_type == 'application/octet-stream':
|
||||
if _file_looks_like_text(file_name):
|
||||
return 'text/plain'
|
||||
else:
|
||||
return 'application/octet-stream'
|
||||
return mime_type
|
||||
|
||||
def get_from_file_name(file_name):
|
||||
return _sugarext.get_mime_type_from_file_name(file_name)
|
||||
@ -51,13 +58,9 @@ def choose_most_significant(mime_types):
|
||||
if 'text/uri-list' in mime_types:
|
||||
return 'text/uri-list'
|
||||
|
||||
for mime_category in ['image/', 'text/', 'application/']:
|
||||
for mime_category in ['image/', 'application/']:
|
||||
for mime_type in mime_types:
|
||||
|
||||
# skip text/plain and text/html, these have lower priority.
|
||||
if mime_type in ['text/plain', 'text/html']:
|
||||
continue
|
||||
|
||||
if mime_type.startswith(mime_category):
|
||||
# skip mozilla private types (second component starts with '_'
|
||||
# or ends with '-priv')
|
||||
@ -70,6 +73,10 @@ def choose_most_significant(mime_types):
|
||||
logging.debug('Choosed %r!' % mime_type)
|
||||
return mime_type
|
||||
|
||||
if 'text/x-moz-url' in mime_types:
|
||||
logging.debug('Choosed text/x-moz-url!')
|
||||
return 'text/x-moz-url'
|
||||
|
||||
if 'text/html' in mime_types:
|
||||
logging.debug('Choosed text/html!')
|
||||
return 'text/html'
|
||||
@ -80,3 +87,22 @@ def choose_most_significant(mime_types):
|
||||
|
||||
logging.debug('Returning first: %r.' % mime_types[0])
|
||||
return mime_types[0]
|
||||
|
||||
def _file_looks_like_text(file_name):
|
||||
f = open(file_name, 'r')
|
||||
try:
|
||||
sample = f.read(256)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
if '\000' in sample:
|
||||
return False
|
||||
|
||||
for encoding in ('ascii', 'latin_1', 'utf_8', 'utf_16'):
|
||||
try:
|
||||
string = unicode(sample, encoding)
|
||||
return True
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
@ -3,5 +3,6 @@ sugar_PYTHON = \
|
||||
__init__.py \
|
||||
activity.py \
|
||||
buddy.py \
|
||||
tubeconn.py \
|
||||
presenceservice.py
|
||||
|
||||
|
107
sugar/presence/tubeconn.py
Normal file
107
sugar/presence/tubeconn.py
Normal file
@ -0,0 +1,107 @@
|
||||
# This should eventually land in telepathy-python, so has the same license:
|
||||
|
||||
# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
|
||||
#
|
||||
# This program 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.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
__all__ = ('TubeConnection',)
|
||||
__docformat__ = 'reStructuredText'
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from dbus.connection import Connection
|
||||
|
||||
|
||||
logger = logging.getLogger('telepathy.tubeconn')
|
||||
|
||||
|
||||
class TubeConnection(Connection):
|
||||
|
||||
def __new__(cls, conn, tubes_iface, tube_id, address=None,
|
||||
group_iface=None, mainloop=None):
|
||||
if address is None:
|
||||
address = tubes_iface.GetDBusServerAddress(tube_id)
|
||||
self = super(TubeConnection, cls).__new__(cls, address,
|
||||
mainloop=mainloop)
|
||||
|
||||
self._tubes_iface = tubes_iface
|
||||
self.tube_id = tube_id
|
||||
self.participants = {}
|
||||
self.bus_name_to_handle = {}
|
||||
self._mapping_watches = []
|
||||
|
||||
if group_iface is None:
|
||||
method = conn.GetSelfHandle
|
||||
else:
|
||||
method = group_iface.GetSelfHandle
|
||||
method(reply_handler=self._on_get_self_handle_reply,
|
||||
error_handler=self._on_get_self_handle_error)
|
||||
|
||||
return self
|
||||
|
||||
def _on_get_self_handle_reply(self, handle):
|
||||
self.self_handle = handle
|
||||
match = self._tubes_iface.connect_to_signal('DBusNamesChanged',
|
||||
self._on_dbus_names_changed)
|
||||
self._tubes_iface.GetDBusNames(self.tube_id,
|
||||
reply_handler=self._on_get_dbus_names_reply,
|
||||
error_handler=self._on_get_dbus_names_error)
|
||||
self._dbus_names_changed_match = match
|
||||
|
||||
def _on_get_self_handle_error(self, e):
|
||||
logging.basicConfig()
|
||||
logger.error('GetSelfHandle failed: %s', e)
|
||||
|
||||
def close(self):
|
||||
self._dbus_names_changed_match.remove()
|
||||
self._on_dbus_names_changed(self.tube_id, (), self.participants.keys())
|
||||
super(TubeConnection, self).close()
|
||||
|
||||
def _on_get_dbus_names_reply(self, names):
|
||||
self._on_dbus_names_changed(self.tube_id, names, ())
|
||||
|
||||
def _on_get_dbus_names_error(self, e):
|
||||
logging.basicConfig()
|
||||
logger.error('GetDBusNames failed: %s', e)
|
||||
|
||||
def _on_dbus_names_changed(self, tube_id, added, removed):
|
||||
if tube_id == self.tube_id:
|
||||
for handle, bus_name in added:
|
||||
if handle == self.self_handle:
|
||||
# I've just joined - set my unique name
|
||||
self.set_unique_name(bus_name)
|
||||
self.participants[handle] = bus_name
|
||||
self.bus_name_to_handle[bus_name] = handle
|
||||
|
||||
# call the callback while the removed people are still in
|
||||
# participants, so their bus names are available
|
||||
for callback in self._mapping_watches:
|
||||
callback(added, removed)
|
||||
|
||||
for handle in removed:
|
||||
bus_name = self.participants.pop(handle, None)
|
||||
self.bus_name_to_handle.pop(bus_name, None)
|
||||
|
||||
def watch_participants(self, callback):
|
||||
self._mapping_watches.append(callback)
|
||||
if self.participants:
|
||||
# GetDBusNames already returned: fake a participant add event
|
||||
# immediately
|
||||
added = []
|
||||
for k, v in self.participants.iteritems():
|
||||
added.append((k, v))
|
||||
callback(added, [])
|
55
tests/graphics/common.py
Normal file
55
tests/graphics/common.py
Normal file
@ -0,0 +1,55 @@
|
||||
# Copyright (C) 2007, 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 gtk
|
||||
|
||||
from sugar.graphics.toolbutton import ToolButton
|
||||
|
||||
class Test(gtk.VBox):
|
||||
def __init__(self):
|
||||
gtk.VBox.__init__(self)
|
||||
|
||||
class TestPalette(Test):
|
||||
def __init__(self):
|
||||
Test.__init__(self)
|
||||
|
||||
toolbar = gtk.Toolbar()
|
||||
|
||||
self._invoker = ToolButton('go-previous')
|
||||
toolbar.insert(self._invoker, -1)
|
||||
self._invoker.show()
|
||||
|
||||
self.pack_start(toolbar, False)
|
||||
toolbar.show()
|
||||
|
||||
def set_palette(self, palette):
|
||||
self._invoker.set_palette(palette)
|
||||
|
||||
class TestRunner(object):
|
||||
def run(self, test):
|
||||
window = gtk.Window()
|
||||
window.connect("destroy", lambda w: gtk.main_quit())
|
||||
window.add(test)
|
||||
test.show()
|
||||
|
||||
window.show()
|
||||
|
||||
def main(test):
|
||||
runner = TestRunner()
|
||||
runner.run(test)
|
||||
|
||||
gtk.main()
|
59
tests/graphics/ticket2855.py
Normal file
59
tests/graphics/ticket2855.py
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2007, 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.
|
||||
|
||||
"""
|
||||
Test the style of toggle and radio buttons inside a palette. The buttons
|
||||
contains only an icon and should be rendered similarly to the toolbar
|
||||
controls. Ticket #2855.
|
||||
"""
|
||||
|
||||
import gtk
|
||||
|
||||
from sugar.graphics.palette import Palette
|
||||
from sugar.graphics.icon import Icon
|
||||
|
||||
import common
|
||||
|
||||
test = common.TestPalette()
|
||||
|
||||
palette = Palette('Test radio and toggle')
|
||||
test.set_palette(palette)
|
||||
|
||||
box = gtk.HBox()
|
||||
|
||||
toggle = gtk.ToggleButton()
|
||||
|
||||
icon = Icon('go-previous', icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
toggle.set_image(icon)
|
||||
|
||||
box.pack_start(toggle, False)
|
||||
toggle.show()
|
||||
|
||||
radio = gtk.RadioButton()
|
||||
|
||||
icon = Icon('go-next', icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
radio.set_image(icon)
|
||||
|
||||
radio.set_mode(False)
|
||||
box.pack_start(radio, False)
|
||||
radio.show()
|
||||
|
||||
palette.set_content(box)
|
||||
box.show()
|
||||
|
||||
if __name__ == "__main__":
|
||||
common.main(test)
|
30
tests/lib/runall.py
Normal file
30
tests/lib/runall.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2007, 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 unittest
|
||||
|
||||
import test_date
|
||||
import test_mime
|
||||
|
||||
runner = unittest.TextTestRunner()
|
||||
loader = unittest.TestLoader()
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(loader.loadTestsFromModule(test_date))
|
||||
suite.addTest(loader.loadTestsFromModule(test_mime))
|
||||
|
||||
runner.run(suite)
|
30
tests/lib/test_date.py
Normal file
30
tests/lib/test_date.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2007, 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 datetime
|
||||
import unittest
|
||||
|
||||
from sugar.date import Date
|
||||
|
||||
class TestDate(unittest.TestCase):
|
||||
def test_today(self):
|
||||
date = Date(datetime.date(2000, 1, 1))
|
||||
date._today = datetime.date(2000, 1, 1)
|
||||
self.assertEqual(str(date), 'Today')
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
72
tests/lib/test_mime.py
Normal file
72
tests/lib/test_mime.py
Normal file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (C) 2006, Red Hat, Inc.
|
||||
# Copyright (C) 2007, One Laptop Per Child
|
||||
#
|
||||
# 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 unittest
|
||||
|
||||
from sugar import objects
|
||||
|
||||
class TestMime(unittest.TestCase):
|
||||
def test_from_file_name(self):
|
||||
self.assertEqual(objects.mime.get_from_file_name('test.pdf'),
|
||||
'application/pdf')
|
||||
|
||||
def test_choose_most_significant(self):
|
||||
# Mozilla's text in dnd
|
||||
mime_type = objects.mime.choose_most_significant(
|
||||
['text/plain', 'text/_moz_htmlcontext', 'text/unicode',
|
||||
'text/html', 'text/_moz_htmlinfo'])
|
||||
self.assertEqual(mime_type, 'text/html')
|
||||
|
||||
# Mozilla's text in c&v
|
||||
mime_type = objects.mime.choose_most_significant(
|
||||
['text/_moz_htmlcontext', 'STRING', 'text/html', 'text/_moz_htmlinfo',
|
||||
'text/x-moz-url-priv', 'UTF8_STRING', 'COMPOUND_TEXT'])
|
||||
self.assertEqual(mime_type, 'text/html')
|
||||
|
||||
# Mozilla gif in dnd
|
||||
mime_type = objects.mime.choose_most_significant(
|
||||
['application/x-moz-file-promise-url',
|
||||
'application/x-moz-file-promise-dest-filename', 'text/_moz_htmlinfo',
|
||||
'text/x-moz-url-desc', 'text/_moz_htmlcontext', 'text/x-moz-url-data',
|
||||
'text/uri-list'])
|
||||
self.assertEqual(mime_type, 'text/uri-list')
|
||||
|
||||
# Mozilla url in dnd
|
||||
mime_type = objects.mime.choose_most_significant(
|
||||
['text/_moz_htmlcontext', 'text/html', 'text/_moz_htmlinfo',
|
||||
'_NETSCAPE_URL', 'text/x-moz-url', 'text/x-moz-url-desc',
|
||||
'text/x-moz-url-data', 'text/plain', 'text/unicode'])
|
||||
self.assertEqual(mime_type, 'text/x-moz-url')
|
||||
|
||||
# Abiword text in dnd
|
||||
mime_type = objects.mime.choose_most_significant(
|
||||
['text/rtf', 'text/uri-list'])
|
||||
self.assertEqual(mime_type, 'text/uri-list')
|
||||
|
||||
# Abiword text in c&v
|
||||
mime_type = objects.mime.choose_most_significant(
|
||||
['UTF8_STRING', 'STRING', 'text/html', 'TEXT', 'text/rtf',
|
||||
'COMPOUND_TEXT', 'application/rtf', 'text/plain',
|
||||
'application/xhtml+xml'])
|
||||
self.assertEqual(mime_type, 'application/rtf')
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
@ -1,136 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (C) 2007, 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 os, time, sys
|
||||
import dbus, dbus.glib
|
||||
import gobject
|
||||
|
||||
from sugar.presence import presenceservice
|
||||
from sugar.p2p import network
|
||||
|
||||
class ReadHTTPRequestHandler(network.ChunkedGlibHTTPRequestHandler):
|
||||
def translate_path(self, path):
|
||||
return self.server._filepath
|
||||
|
||||
class ReadHTTPServer(network.GlibTCPServer):
|
||||
def __init__(self, server_address, request_handler, filepath):
|
||||
self._filepath = filepath
|
||||
network.GlibTCPServer.__init__(self, server_address, request_handler);
|
||||
|
||||
class XMLRPCResponder(object):
|
||||
def __init__(self, have_file=False):
|
||||
self._have_file = have_file
|
||||
|
||||
def _set_have_file(self):
|
||||
self._have_file = True
|
||||
|
||||
def have_file(self):
|
||||
return self._have_file
|
||||
|
||||
|
||||
class MockReadActivity(gobject.GObject):
|
||||
__gproperties__ = {
|
||||
'title' : (str, None, None, None, gobject.PARAM_READABLE)
|
||||
}
|
||||
|
||||
def __init__(self, filepath):
|
||||
self._actid = "ef60b3af42f7b5aa558ef9269e2ed7998798afc0"
|
||||
self._name = "Test Read Activity"
|
||||
self._type = "org.laptop.sugar.ReadActivity"
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self._ps_act = None
|
||||
self._filepath = os.path.abspath(filepath)
|
||||
self._file_server = ReadHTTPServer(("", 8867), ReadHTTPRequestHandler, self._filepath)
|
||||
|
||||
self._xmlrpc_server = network.GlibXMLRPCServer(("", 8868))
|
||||
responder = XMLRPCResponder(have_file=True)
|
||||
self._xmlrpc_server.register_instance(responder)
|
||||
|
||||
def _activity_appeared_cb(self, ps, activity):
|
||||
if activity.props.id != self._actid:
|
||||
return
|
||||
self._ps_act = activity
|
||||
|
||||
def share(self):
|
||||
ps = presenceservice.get_instance()
|
||||
ps.connect("activity-appeared", self._activity_appeared_cb)
|
||||
ps.share_activity(self)
|
||||
return False
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == "title":
|
||||
return self._name
|
||||
|
||||
def get_id(self):
|
||||
return self._actid
|
||||
|
||||
def get_service_name(self):
|
||||
return self._type
|
||||
|
||||
def start_ps():
|
||||
import commands
|
||||
(s, o) = commands.getstatusoutput("which sugar-presence-service")
|
||||
if s != 0:
|
||||
raise RuntimeError("Failed to find sugar presence service: %s" % o)
|
||||
argv = [o, "1"]
|
||||
(pid, stdin, stdout, stderr) = gobject.spawn_async(argv, flags=gobject.SPAWN_LEAVE_DESCRIPTORS_OPEN)
|
||||
|
||||
# Wait until it shows up on the bus
|
||||
tries = 0
|
||||
bus = dbus.SessionBus()
|
||||
while tries < 10:
|
||||
time.sleep(0.5)
|
||||
bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
||||
try:
|
||||
if bus_object.GetNameOwner(presenceservice.DBUS_SERVICE, dbus_interface='org.freedesktop.DBus'):
|
||||
break
|
||||
except dbus.exceptions.DBusException, err:
|
||||
pass
|
||||
tries += 1
|
||||
|
||||
if tries >= 5:
|
||||
stop_ps(pid)
|
||||
raise RuntimeError("Couldn't start the mock presence service")
|
||||
|
||||
print "Started presence service PID %d" % pid
|
||||
return pid
|
||||
|
||||
|
||||
def stop_ps(pid):
|
||||
if pid >= 0:
|
||||
os.kill(pid, 15)
|
||||
print "Stopped presence service PID %d" % pid
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
raise RuntimeError("Must specify a PDF to share.")
|
||||
path = os.path.abspath(sys.argv[1])
|
||||
if not os.path.exists(path):
|
||||
raise RuntimeError("File %s doesn't exist." % path)
|
||||
mact = MockReadActivity(path)
|
||||
pid = start_ps()
|
||||
loop = gobject.MainLoop()
|
||||
gobject.timeout_add(2000, mact.share)
|
||||
try:
|
||||
loop.run()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
stop_ps(pid)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,422 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (C) 2007, 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 gobject
|
||||
import dbus, dbus.service, dbus.glib
|
||||
|
||||
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
|
||||
_PRESENCE_INTERFACE = "org.laptop.Sugar.Presence"
|
||||
_PRESENCE_TEST_INTERFACE = "org.laptop.Sugar.Presence._Test"
|
||||
_PRESENCE_PATH = "/org/laptop/Sugar/Presence"
|
||||
|
||||
|
||||
class NotFoundError(dbus.DBusException):
|
||||
def __init__(self, msg=None):
|
||||
dbus.DBusException.__init__(self, msg)
|
||||
self._dbus_error_name = _PRESENCE_INTERFACE + '.NotFound'
|
||||
|
||||
|
||||
_ACTIVITY_PATH = "/org/laptop/Sugar/Presence/Activities/"
|
||||
_ACTIVITY_INTERFACE = "org.laptop.Sugar.Presence.Activity"
|
||||
|
||||
class TestActivity(dbus.service.Object):
|
||||
def __init__(self, bus_name, object_id, parent, actid, name, color, atype, properties):
|
||||
self._parent = parent
|
||||
self._actid = actid
|
||||
self._aname = name
|
||||
self._color = color
|
||||
self._type = atype
|
||||
self._properties = {}
|
||||
for (key, value) in properties.items():
|
||||
self._properties[str(key)] = str(value)
|
||||
self._buddies = {}
|
||||
|
||||
self._object_id = object_id
|
||||
self._object_path = _ACTIVITY_PATH + str(self._object_id)
|
||||
dbus.service.Object.__init__(self, bus_name, self._object_path)
|
||||
|
||||
def add_buddy(self, buddy):
|
||||
if self._buddies.has_key(buddy._key):
|
||||
raise NotFoundError("Buddy already in activity")
|
||||
self._buddies[buddy._key] = buddy
|
||||
self.BuddyJoined(buddy._object_path)
|
||||
|
||||
def remove_buddy(self, buddy):
|
||||
if not self._buddies.has_key(buddy._key):
|
||||
raise NotFoundError("Buddy not in activity")
|
||||
self.BuddyLeft(buddy._object_path)
|
||||
del self._buddies[buddy._key]
|
||||
|
||||
def disappear(self):
|
||||
# remove all buddies from activity
|
||||
for buddy in self.get_buddies():
|
||||
self.BuddyLeft(buddy._object_path)
|
||||
self._buddies = {}
|
||||
|
||||
def get_buddies(self):
|
||||
return self._buddies.values()
|
||||
|
||||
@dbus.service.signal(_ACTIVITY_INTERFACE, signature="o")
|
||||
def BuddyJoined(self, buddy_path):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_ACTIVITY_INTERFACE, signature="o")
|
||||
def BuddyLeft(self, buddy_path):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_ACTIVITY_INTERFACE, signature="o")
|
||||
def NewChannel(self, channel_path):
|
||||
pass
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE, out_signature="s")
|
||||
def GetId(self):
|
||||
return self._actid
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE, out_signature="s")
|
||||
def GetName(self):
|
||||
return self._aname
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE, out_signature="s")
|
||||
def GetColor(self):
|
||||
return self._color
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE, out_signature="s")
|
||||
def GetType(self):
|
||||
return self._type
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE)
|
||||
def Join(self):
|
||||
owner = self._parent._owner
|
||||
self.add_buddy(owner)
|
||||
owner.add_activity(self)
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE, out_signature="ao")
|
||||
def GetJoinedBuddies(self):
|
||||
ret = []
|
||||
for buddy in self._buddies.values():
|
||||
ret.append(dbus.ObjectPath(buddy._object_path))
|
||||
return ret
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE, out_signature="soao")
|
||||
def GetChannels(self):
|
||||
return None
|
||||
|
||||
|
||||
_BUDDY_PATH = "/org/laptop/Sugar/Presence/Buddies/"
|
||||
_BUDDY_INTERFACE = "org.laptop.Sugar.Presence.Buddy"
|
||||
_OWNER_INTERFACE = "org.laptop.Sugar.Presence.Buddy.Owner"
|
||||
|
||||
_PROP_NICK = "nick"
|
||||
_PROP_KEY = "key"
|
||||
_PROP_ICON = "icon"
|
||||
_PROP_CURACT = "current-activity"
|
||||
_PROP_COLOR = "color"
|
||||
_PROP_OWNER = "owner"
|
||||
|
||||
class TestBuddy(dbus.service.Object):
|
||||
def __init__(self, bus_name, object_id, pubkey, nick, color):
|
||||
self._key = pubkey
|
||||
self._nick = nick
|
||||
self._color = color
|
||||
self._owner = False
|
||||
self._curact = None
|
||||
self._icon = ""
|
||||
self._activities = {}
|
||||
|
||||
self._object_id = object_id
|
||||
self._object_path = _BUDDY_PATH + str(self._object_id)
|
||||
dbus.service.Object.__init__(self, bus_name, self._object_path)
|
||||
|
||||
def add_activity(self, activity):
|
||||
if self._activities.has_key(activity._actid):
|
||||
raise NotFoundError("Buddy already in activity")
|
||||
self._activities[activity._actid] = activity
|
||||
self.JoinedActivity(activity._object_path)
|
||||
|
||||
def remove_activity(self, activity):
|
||||
if not self._activities.has_key(activity._actid):
|
||||
raise NotFoundError("Buddy not in activity")
|
||||
self.LeftActivity(activity._object_path)
|
||||
del self._activities[activity._actid]
|
||||
|
||||
def leave_activities(self):
|
||||
for activity in self.get_activities():
|
||||
self.LeftActivity(activity._object_path)
|
||||
self._activities = {}
|
||||
|
||||
def get_activities(self):
|
||||
return self._activities.values()
|
||||
|
||||
def set_current_activity(self, actid):
|
||||
self._curact = actid
|
||||
self.PropertyChanged({_PROP_CURACT: actid})
|
||||
|
||||
@dbus.service.signal(_BUDDY_INTERFACE, signature="ay")
|
||||
def IconChanged(self, icon_data):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_BUDDY_INTERFACE, signature="o")
|
||||
def JoinedActivity(self, activity_path):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_BUDDY_INTERFACE, signature="o")
|
||||
def LeftActivity(self, activity_path):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_BUDDY_INTERFACE, signature="a{sv}")
|
||||
def PropertyChanged(self, updated):
|
||||
pass
|
||||
|
||||
# dbus methods
|
||||
@dbus.service.method(_BUDDY_INTERFACE, in_signature="", out_signature="ay")
|
||||
def GetIcon(self):
|
||||
return dbus.ByteArray(self._icon)
|
||||
|
||||
@dbus.service.method(_BUDDY_INTERFACE, in_signature="", out_signature="ao")
|
||||
def GetJoinedActivities(self):
|
||||
acts = []
|
||||
for activity in self._activities.values():
|
||||
acts.append(dbus.ObjectPath(activity._object_path))
|
||||
return acts
|
||||
|
||||
@dbus.service.method(_BUDDY_INTERFACE, in_signature="", out_signature="a{sv}")
|
||||
def GetProperties(self):
|
||||
props = {}
|
||||
props[_PROP_NICK] = self._nick
|
||||
props[_PROP_OWNER] = self._owner
|
||||
props[_PROP_KEY] = self._key
|
||||
props[_PROP_COLOR] = self._color
|
||||
if self._curact:
|
||||
props[_PROP_CURACT] = self._curact
|
||||
else:
|
||||
props[_PROP_CURACT] = ""
|
||||
return props
|
||||
|
||||
_OWNER_PUBKEY = "AAAAB3NzaC1kc3MAAACBAKEVDFJW9D9GK20QFYRKbhV7kpjnhKkkzudn34ij" \
|
||||
"Ixje+x1ZXTIU6J1GFmJYrHq9uBRi72lOVAosGUop+HHZFRyTeYLxItmKfIoD" \
|
||||
"S2rwyL9cGRoDsD4yjECMqa2I+pGxriw4OmHeu5vmBkk+5bXBdkLf0EfseuPC" \
|
||||
"lT7FE+Fj4C6FAAAAFQCygOIpXXybKlVTcEfprOQp3Uud0QAAAIBjyjQhOWHq" \
|
||||
"FdJlALmnriQR+Zi1i4N/UMjWihF245RXJuUU6DyYbx4QxznxRnYKx/ZvsD0O" \
|
||||
"9+ihzmQd6eFwU/jQ6sxiL7DSlCJ3axgG9Yvbf7ELeXGo4/Z9keOVdei0sXz4" \
|
||||
"VBvJC0c0laELsnU0spFC62qQKxNemTbXDGksauj19gAAAIEAmcvY8VX47pRP" \
|
||||
"k7MjrDzZlPvvNQgHMNZSwHGIsF7EMGVDCYpbQTyR+cmtJBBFVyxtNbK7TWTZ" \
|
||||
"K8uH1tm9GyMcViUdIT4xCirA0JanE597KdlBz39l/623wF4jvbnnHOZ/pIT9" \
|
||||
"tPd1pCYJf+L7OEKCBUAyQhcq159X8A1toM48Soc="
|
||||
_OWNER_PRIVKEY = "MIIBuwIBAAKBgQChFQxSVvQ/RittEBWESm4Ve5KY54SpJM7nZ9+IoyMY3vs" \
|
||||
"dWV0yFOidRhZiWKx6vbgUYu9pTlQKLBlKKfhx2RUck3mC8SLZinyKA0tq8M" \
|
||||
"i/XBkaA7A+MoxAjKmtiPqRsa4sODph3rub5gZJPuW1wXZC39BH7HrjwpU+x" \
|
||||
"RPhY+AuhQIVALKA4ildfJsqVVNwR+ms5CndS53RAoGAY8o0ITlh6hXSZQC5" \
|
||||
"p64kEfmYtYuDf1DI1ooRduOUVyblFOg8mG8eEMc58UZ2Csf2b7A9Dvfooc5" \
|
||||
"kHenhcFP40OrMYi+w0pQid2sYBvWL23+xC3lxqOP2fZHjlXXotLF8+FQbyQ" \
|
||||
"tHNJWhC7J1NLKRQutqkCsTXpk21wxpLGro9fYCgYEAmcvY8VX47pRPk7Mjr" \
|
||||
"DzZlPvvNQgHMNZSwHGIsF7EMGVDCYpbQTyR+cmtJBBFVyxtNbK7TWTZK8uH" \
|
||||
"1tm9GyMcViUdIT4xCirA0JanE597KdlBz39l/623wF4jvbnnHOZ/pIT9tPd" \
|
||||
"1pCYJf+L7OEKCBUAyQhcq159X8A1toM48SocCFAvkZYCYtLhSDEPrlf0jLD" \
|
||||
"jrMz+i"
|
||||
_OWNER_NICK = "TestOwner"
|
||||
_OWNER_COLOR = "#75C228,#308C30"
|
||||
|
||||
class TestOwner(TestBuddy):
|
||||
def __init__(self, bus_name, object_id):
|
||||
TestBuddy.__init__(self, bus_name, object_id, _OWNER_PUBKEY,
|
||||
_OWNER_NICK, _OWNER_COLOR)
|
||||
self._owner = True
|
||||
|
||||
|
||||
class TestPresenceService(dbus.service.Object):
|
||||
"""A test D-Bus PresenceService used to exercise the Sugar PS bindings."""
|
||||
|
||||
def __init__(self):
|
||||
self._next_object_id = 0
|
||||
self._activities = {}
|
||||
self._buddies = {}
|
||||
|
||||
self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE,
|
||||
bus=dbus.SessionBus())
|
||||
|
||||
objid = self._get_next_object_id()
|
||||
self._owner = TestOwner(self._bus_name, objid)
|
||||
|
||||
dbus.service.Object.__init__(self, self._bus_name, _PRESENCE_PATH)
|
||||
|
||||
def _get_next_object_id(self):
|
||||
"""Increment and return the object ID counter."""
|
||||
self._next_object_id = self._next_object_id + 1
|
||||
return self._next_object_id
|
||||
|
||||
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
|
||||
def ActivityAppeared(self, activity):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
|
||||
def ActivityDisappeared(self, activity):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
|
||||
def BuddyAppeared(self, buddy):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
|
||||
def BuddyDisappeared(self, buddy):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
|
||||
def ActivityInvitation(self, activity):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(_PRESENCE_INTERFACE, signature="soo")
|
||||
def PrivateInvitation(self, bus_name, connection, channel):
|
||||
pass
|
||||
|
||||
@dbus.service.method(_PRESENCE_INTERFACE, out_signature="ao")
|
||||
def GetActivities(self):
|
||||
ret = []
|
||||
for act in self._activities.values():
|
||||
ret.append(dbus.ObjectPath(act._object_path))
|
||||
return ret
|
||||
|
||||
@dbus.service.method(_PRESENCE_INTERFACE, in_signature="s", out_signature="o")
|
||||
def GetActivityById(self, actid):
|
||||
if self._activities.has_key(actid):
|
||||
return dbus.ObjectPath(self._activities[actid]._object_path)
|
||||
raise NotFoundError("The activity was not found.")
|
||||
|
||||
@dbus.service.method(_PRESENCE_INTERFACE, out_signature="ao")
|
||||
def GetBuddies(self):
|
||||
ret = []
|
||||
for buddy in self._buddies.values():
|
||||
ret.append(buddy._object_path)
|
||||
return ret
|
||||
|
||||
@dbus.service.method(_PRESENCE_INTERFACE, in_signature="ay", out_signature="o")
|
||||
def GetBuddyByPublicKey(self, key):
|
||||
key = ''.join([chr(item) for item in key])
|
||||
if self._buddies.has_key(key):
|
||||
return self._buddies[key]._object_path
|
||||
raise NotFoundError("The buddy was not found.")
|
||||
|
||||
@dbus.service.method(_PRESENCE_INTERFACE, out_signature="o")
|
||||
def GetOwner(self):
|
||||
if not self._owner:
|
||||
raise NotFoundError("The owner was not found.")
|
||||
return dbus.ObjectPath(self._owner._object_path)
|
||||
|
||||
def _internal_share_activity(self, actid, atype, name, properties, color=None):
|
||||
objid = self._get_next_object_id()
|
||||
if not color:
|
||||
color = self._owner._color
|
||||
act = TestActivity(self._bus_name, objid, self, actid, name, color, atype, properties)
|
||||
self._activities[actid] = act
|
||||
self.ActivityAppeared(act._object_path)
|
||||
return act
|
||||
|
||||
@dbus.service.method(_PRESENCE_INTERFACE, in_signature="sssa{sv}",
|
||||
out_signature="o")
|
||||
def ShareActivity(self, actid, atype, name, properties):
|
||||
act = self._internal_share_activity(actid, atype, name, properties)
|
||||
act.add_buddy(self._owner)
|
||||
self._owner.add_activity(act)
|
||||
return act._object_path
|
||||
|
||||
@dbus.service.method(_PRESENCE_INTERFACE, out_signature="so")
|
||||
def GetPreferredConnection(self):
|
||||
return "bar.baz.foo", "/bar/baz/foo"
|
||||
|
||||
# Private methods used for testing
|
||||
@dbus.service.method(_PRESENCE_TEST_INTERFACE, in_signature="ayss")
|
||||
def AddBuddy(self, pubkey, nick, color):
|
||||
pubkey = ''.join([chr(item) for item in pubkey])
|
||||
objid = self._get_next_object_id()
|
||||
buddy = TestBuddy(self._bus_name, objid, pubkey, nick, color)
|
||||
self._buddies[pubkey] = buddy
|
||||
self.BuddyAppeared(buddy._object_path)
|
||||
|
||||
@dbus.service.method(_PRESENCE_TEST_INTERFACE, in_signature="ay")
|
||||
def RemoveBuddy(self, pubkey):
|
||||
pubkey = ''.join([chr(item) for item in pubkey])
|
||||
if not self._buddies.has_key(pubkey):
|
||||
raise NotFoundError("Buddy not found")
|
||||
buddy = self._buddies[pubkey]
|
||||
activities = buddy.get_activities()
|
||||
# remove activity from the buddy
|
||||
buddy.leave_activities()
|
||||
# remove the buddy from all activities
|
||||
for act in activities:
|
||||
act.remove_buddy(buddy)
|
||||
self.BuddyDisappeared(buddy._object_path)
|
||||
del self._buddies[pubkey]
|
||||
|
||||
@dbus.service.method(_PRESENCE_TEST_INTERFACE, in_signature="ays")
|
||||
def AddBuddyToActivity(self, pubkey, actid):
|
||||
pubkey = ''.join([chr(item) for item in pubkey])
|
||||
if not self._buddies.has_key(pubkey):
|
||||
raise NotFoundError("Buddy unknown")
|
||||
if not self._activities.has_key(actid):
|
||||
raise NotFoundError("Activity unknown")
|
||||
|
||||
buddy = self._buddies[pubkey]
|
||||
activity = self._activities[actid]
|
||||
activity.add_buddy(buddy)
|
||||
buddy.add_activity(activity)
|
||||
|
||||
@dbus.service.method(_PRESENCE_TEST_INTERFACE, in_signature="ays")
|
||||
def RemoveBuddyFromActivity(self, pubkey, actid):
|
||||
pubkey = ''.join([chr(item) for item in pubkey])
|
||||
if not self._buddies.has_key(pubkey):
|
||||
raise NotFoundError("Buddy unknown")
|
||||
if not self._activities.has_key(actid):
|
||||
raise NotFoundError("Activity unknown")
|
||||
|
||||
buddy = self._buddies[pubkey]
|
||||
activity = self._activities[actid]
|
||||
buddy.remove_activity(activity)
|
||||
activity.remove_buddy(buddy)
|
||||
|
||||
@dbus.service.method(_PRESENCE_TEST_INTERFACE, in_signature="ssssa{sv}")
|
||||
def AddActivity(self, actid, name, color, atype, properties):
|
||||
self._internal_share_activity(actid, atype, name, properties, color=color)
|
||||
|
||||
@dbus.service.method(_PRESENCE_TEST_INTERFACE, in_signature="s")
|
||||
def RemoveActivity(self, actid):
|
||||
if not self._activities.has_key(actid):
|
||||
raise NotFoundError("Activity not found")
|
||||
act = self._activities[actid]
|
||||
# remove activity from all buddies
|
||||
for buddy in act.get_buddies():
|
||||
buddy.remove_activity(act)
|
||||
act.disappear()
|
||||
self.ActivityDisappeared(act._object_path)
|
||||
del self._activities[actid]
|
||||
|
||||
@dbus.service.method(_PRESENCE_TEST_INTERFACE, in_signature="ays")
|
||||
def SetBuddyCurrentActivity(self, pubkey, actid):
|
||||
pubkey = ''.join([chr(item) for item in pubkey])
|
||||
if not self._buddies.has_key(pubkey):
|
||||
raise NotFoundError("Buddy unknown")
|
||||
buddy = self._buddies[pubkey]
|
||||
buddy.set_current_activity(actid)
|
||||
|
||||
def main():
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
loop = gobject.MainLoop()
|
||||
ps = TestPresenceService()
|
||||
loop.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,723 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (C) 2007, 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 os, time
|
||||
import dbus
|
||||
import gobject, gtk
|
||||
import unittest
|
||||
from sugar.presence import presenceservice
|
||||
|
||||
import mockps
|
||||
|
||||
def start_ps():
|
||||
argv = ["mockps.py"]
|
||||
(pid, stdin, stdout, stderr) = gobject.spawn_async(argv, flags=gobject.SPAWN_LEAVE_DESCRIPTORS_OPEN)
|
||||
|
||||
# Wait until it shows up on the bus
|
||||
tries = 0
|
||||
bus = dbus.SessionBus()
|
||||
while tries < 10:
|
||||
time.sleep(0.5)
|
||||
bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
||||
try:
|
||||
if bus_object.GetNameOwner(presenceservice.DBUS_SERVICE, dbus_interface='org.freedesktop.DBus'):
|
||||
break
|
||||
except dbus.exceptions.DBusException, err:
|
||||
pass
|
||||
tries += 1
|
||||
|
||||
if tries >= 5:
|
||||
stop_ps(pid)
|
||||
raise RuntimeError("Couldn't start the mock presence service")
|
||||
|
||||
return pid
|
||||
|
||||
def stop_ps(pid):
|
||||
# EVIL HACK: get a new presence service object every time; close the
|
||||
# connection to completely clear all signal matches too
|
||||
presenceservice._ps._bus.close()
|
||||
del presenceservice._ps
|
||||
presenceservice._ps = None
|
||||
if pid >= 0:
|
||||
os.kill(pid, 15)
|
||||
|
||||
def get_ps():
|
||||
ps = presenceservice.get_instance(False)
|
||||
# HACK
|
||||
# Set exit on disconnect to False so we don't get aborted when
|
||||
# explicitly closing the bus connection in stop_ps()
|
||||
ps._bus.set_exit_on_disconnect(False)
|
||||
return ps
|
||||
|
||||
|
||||
class GenericTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._pspid = start_ps()
|
||||
self._success = False
|
||||
self._err = ""
|
||||
self._signals = []
|
||||
self._sources = []
|
||||
|
||||
def tearDown(self):
|
||||
# Remove all signal handlers
|
||||
for (obj, sid) in self._signals:
|
||||
obj.disconnect(sid)
|
||||
for source in self._sources:
|
||||
gobject.source_remove(source)
|
||||
|
||||
if self._pspid > 0:
|
||||
stop_ps(self._pspid)
|
||||
self._pspid = -1
|
||||
|
||||
def _handle_success(self):
|
||||
self._success = True
|
||||
gtk.main_quit()
|
||||
|
||||
def _handle_error(self, err):
|
||||
self._success = False
|
||||
self._err = str(err)
|
||||
gtk.main_quit()
|
||||
|
||||
class BuddyTests(GenericTestCase):
|
||||
def _testOwner_helper(self):
|
||||
try:
|
||||
ps = get_ps()
|
||||
except RuntimeError, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
try:
|
||||
owner = ps.get_owner()
|
||||
except RuntimeError, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
self._owner = owner
|
||||
self._handle_success()
|
||||
return False
|
||||
|
||||
def testOwner(self):
|
||||
gobject.idle_add(self._testOwner_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful."
|
||||
assert self._owner, "Owner could not be found."
|
||||
|
||||
assert self._owner.props.key == mockps._OWNER_PUBKEY, "Owner public key doesn't match expected"
|
||||
assert self._owner.props.nick == mockps._OWNER_NICK, "Owner nickname doesn't match expected"
|
||||
assert self._owner.props.color == mockps._OWNER_COLOR, "Owner color doesn't match expected"
|
||||
|
||||
_BA_PUBKEY = "akjadskjjfahfdahfdsahjfhfewaew3253232832832q098qewa98fdsafa98fa"
|
||||
_BA_NICK = "BuddyAppearedTestBuddy"
|
||||
_BA_COLOR = "#23adfb,#56bb11"
|
||||
|
||||
def _testBuddyAppeared_helper_timeout(self):
|
||||
self._handle_error("Timeout waiting for buddy-appeared signal")
|
||||
return False
|
||||
|
||||
def _testBuddyAppeared_helper_cb(self, ps, buddy):
|
||||
self._buddy = buddy
|
||||
self._handle_success()
|
||||
|
||||
def _testBuddyAppeared_helper(self):
|
||||
ps = get_ps()
|
||||
sid = ps.connect('buddy-appeared', self._testBuddyAppeared_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
# Wait 5 seconds max for signal to be emitted
|
||||
sid = gobject.timeout_add(5000, self._testBuddyAppeared_helper_timeout)
|
||||
self._sources.append(sid)
|
||||
|
||||
busobj = dbus.SessionBus().get_object(mockps._PRESENCE_SERVICE,
|
||||
mockps._PRESENCE_PATH)
|
||||
try:
|
||||
testps = dbus.Interface(busobj, mockps._PRESENCE_TEST_INTERFACE)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
try:
|
||||
testps.AddBuddy(self._BA_PUBKEY, self._BA_NICK, self._BA_COLOR)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def testBuddyAppeared(self):
|
||||
ps = get_ps()
|
||||
assert ps, "Couldn't get presence service"
|
||||
|
||||
self._buddy = None
|
||||
gobject.idle_add(self._testBuddyAppeared_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful."
|
||||
assert self._buddy, "Buddy was not received"
|
||||
|
||||
assert self._buddy.props.key == self._BA_PUBKEY, "Public key doesn't match expected"
|
||||
assert self._buddy.props.nick == self._BA_NICK, "Nickname doesn't match expected"
|
||||
assert self._buddy.props.color == self._BA_COLOR, "Color doesn't match expected"
|
||||
|
||||
# Try to get buddy by public key
|
||||
buddy2 = ps.get_buddy(self._BA_PUBKEY)
|
||||
assert buddy2, "Couldn't get buddy by public key"
|
||||
assert buddy2.props.key == self._BA_PUBKEY, "Public key doesn't match expected"
|
||||
assert buddy2.props.nick == self._BA_NICK, "Nickname doesn't match expected"
|
||||
assert buddy2.props.color == self._BA_COLOR, "Color doesn't match expected"
|
||||
|
||||
def _testBuddyDisappeared_helper_timeout(self):
|
||||
self._handle_error("Timeout waiting for buddy-disappeared signal")
|
||||
return False
|
||||
|
||||
def _testBuddyDisappeared_helper_cb(self, ps, buddy):
|
||||
self._buddy = buddy
|
||||
self._handle_success()
|
||||
|
||||
def _testBuddyDisappeared_helper(self):
|
||||
busobj = dbus.SessionBus().get_object(mockps._PRESENCE_SERVICE,
|
||||
mockps._PRESENCE_PATH)
|
||||
try:
|
||||
testps = dbus.Interface(busobj, mockps._PRESENCE_TEST_INTERFACE)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
# Add a fake buddy
|
||||
try:
|
||||
testps.AddBuddy(self._BA_PUBKEY, self._BA_NICK, self._BA_COLOR)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
ps = get_ps()
|
||||
sid = ps.connect('buddy-disappeared', self._testBuddyDisappeared_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
|
||||
# Wait 5 seconds max for signal to be emitted
|
||||
sid = gobject.timeout_add(5000, self._testBuddyDisappeared_helper_timeout)
|
||||
self._sources.append(sid)
|
||||
|
||||
# Delete the fake buddy
|
||||
try:
|
||||
testps.RemoveBuddy(self._BA_PUBKEY)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def testBuddyDisappeared(self):
|
||||
ps = get_ps()
|
||||
assert ps, "Couldn't get presence service"
|
||||
|
||||
self._buddy = None
|
||||
gobject.idle_add(self._testBuddyDisappeared_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful."
|
||||
assert self._buddy, "Buddy was not received"
|
||||
|
||||
assert self._buddy.props.key == self._BA_PUBKEY, "Public key doesn't match expected"
|
||||
assert self._buddy.props.nick == self._BA_NICK, "Nickname doesn't match expected"
|
||||
assert self._buddy.props.color == self._BA_COLOR, "Color doesn't match expected"
|
||||
|
||||
def addToSuite(suite):
|
||||
suite.addTest(BuddyTests("testOwner"))
|
||||
suite.addTest(BuddyTests("testBuddyAppeared"))
|
||||
suite.addTest(BuddyTests("testBuddyDisappeared"))
|
||||
addToSuite = staticmethod(addToSuite)
|
||||
|
||||
class MockSugarActivity(gobject.GObject):
|
||||
__gproperties__ = {
|
||||
'title' : (str, None, None, None, gobject.PARAM_READABLE)
|
||||
}
|
||||
|
||||
def __init__(self, actid, name, atype):
|
||||
self._actid = actid
|
||||
self._name = name
|
||||
self._type = atype
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == "title":
|
||||
return self._name
|
||||
|
||||
def get_id(self):
|
||||
return self._actid
|
||||
|
||||
def get_service_name(self):
|
||||
return self._type
|
||||
|
||||
class ActivityTests(GenericTestCase):
|
||||
_AA_ID = "d622b99b9f365d712296094b9f6110521e6c9cba"
|
||||
_AA_NAME = "Test Activity"
|
||||
_AA_TYPE = "org.laptop.Sugar.Foobar"
|
||||
_AA_COLOR = "#adfe20,#bf781a"
|
||||
_AA_PROPS = {"foo": "asdfadf", "bar":"5323aggdas"}
|
||||
|
||||
def _testActivityAppeared_helper_timeout(self):
|
||||
self._handle_error("Timeout waiting for activity-appeared signal")
|
||||
return False
|
||||
|
||||
def _testActivityAppeared_helper_cb(self, ps, activity):
|
||||
self._activity = activity
|
||||
self._handle_success()
|
||||
|
||||
def _testActivityAppeared_helper(self):
|
||||
ps = get_ps()
|
||||
sid = ps.connect('activity-appeared', self._testActivityAppeared_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
|
||||
# Wait 5 seconds max for signal to be emitted
|
||||
sid = gobject.timeout_add(5000, self._testActivityAppeared_helper_timeout)
|
||||
self._sources.append(sid)
|
||||
|
||||
busobj = dbus.SessionBus().get_object(mockps._PRESENCE_SERVICE,
|
||||
mockps._PRESENCE_PATH)
|
||||
try:
|
||||
testps = dbus.Interface(busobj, mockps._PRESENCE_TEST_INTERFACE)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
try:
|
||||
testps.AddActivity(self._AA_ID, self._AA_NAME, self._AA_COLOR, self._AA_TYPE, {})
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def testActivityAppeared(self):
|
||||
ps = get_ps()
|
||||
assert ps, "Couldn't get presence service"
|
||||
|
||||
self._activity = None
|
||||
gobject.idle_add(self._testActivityAppeared_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful"
|
||||
assert self._activity, "Activity was not received"
|
||||
|
||||
assert self._activity.props.id == self._AA_ID, "ID doesn't match expected"
|
||||
assert self._activity.props.name == self._AA_NAME, "Name doesn't match expected"
|
||||
assert self._activity.props.color == self._AA_COLOR, "Color doesn't match expected"
|
||||
assert self._activity.props.type == self._AA_TYPE, "Type doesn't match expected"
|
||||
assert self._activity.props.joined == False, "Joined doesn't match expected"
|
||||
|
||||
# Try to get activity by activity ID
|
||||
act2 = ps.get_activity(self._AA_ID)
|
||||
assert act2.props.id == self._AA_ID, "ID doesn't match expected"
|
||||
assert act2.props.name == self._AA_NAME, "Name doesn't match expected"
|
||||
assert act2.props.color == self._AA_COLOR, "Color doesn't match expected"
|
||||
assert act2.props.type == self._AA_TYPE, "Type doesn't match expected"
|
||||
assert act2.props.joined == False, "Joined doesn't match expected"
|
||||
|
||||
def _testActivityDisappeared_helper_timeout(self):
|
||||
self._handle_error("Timeout waiting for activity-disappeared signal")
|
||||
return False
|
||||
|
||||
def _testActivityDisappeared_helper_cb(self, ps, activity):
|
||||
self._activity = activity
|
||||
self._handle_success()
|
||||
|
||||
def _testActivityDisappeared_helper(self):
|
||||
busobj = dbus.SessionBus().get_object(mockps._PRESENCE_SERVICE,
|
||||
mockps._PRESENCE_PATH)
|
||||
try:
|
||||
testps = dbus.Interface(busobj, mockps._PRESENCE_TEST_INTERFACE)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
# Add a fake activity
|
||||
try:
|
||||
testps.AddActivity(self._AA_ID, self._AA_NAME, self._AA_COLOR, self._AA_TYPE, {})
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
ps = get_ps()
|
||||
sid = ps.connect('activity-disappeared', self._testActivityDisappeared_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
|
||||
# Wait 5 seconds max for signal to be emitted
|
||||
sid = gobject.timeout_add(5000, self._testActivityDisappeared_helper_timeout)
|
||||
self._sources.append(sid)
|
||||
|
||||
# Delete the fake activity
|
||||
try:
|
||||
testps.RemoveActivity(self._AA_ID)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def testActivityDisappeared(self):
|
||||
ps = get_ps()
|
||||
assert ps, "Couldn't get presence service"
|
||||
|
||||
self._activity = None
|
||||
gobject.idle_add(self._testActivityDisappeared_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful"
|
||||
assert self._activity, "Activity was not received"
|
||||
|
||||
assert self._activity.props.id == self._AA_ID, "ID doesn't match expected"
|
||||
assert self._activity.props.name == self._AA_NAME, "Name doesn't match expected"
|
||||
assert self._activity.props.color == self._AA_COLOR, "Color doesn't match expected"
|
||||
assert self._activity.props.type == self._AA_TYPE, "Type doesn't match expected"
|
||||
assert self._activity.props.joined == False, "Joined doesn't match expected"
|
||||
|
||||
def _testActivityShare_helper_is_done(self):
|
||||
if self._got_act_appeared and self._got_joined_activity:
|
||||
self._handle_success()
|
||||
|
||||
def _testActivityShare_helper_timeout(self):
|
||||
self._handle_error("Timeout waiting for activity share")
|
||||
return False
|
||||
|
||||
def _testActivityShare_helper_joined_activity_cb(self, buddy, activity):
|
||||
self._joined_activity_buddy = buddy
|
||||
self._joined_activity_activity = activity
|
||||
self._got_joined_activity = True
|
||||
self._testActivityShare_helper_is_done()
|
||||
|
||||
def _testActivityShare_helper_cb(self, ps, activity):
|
||||
self._activity = activity
|
||||
self._got_act_appeared = True
|
||||
self._testActivityShare_helper_is_done()
|
||||
|
||||
def _testActivityShare_helper(self):
|
||||
ps = get_ps()
|
||||
mockact = MockSugarActivity(self._AA_ID, self._AA_NAME, self._AA_TYPE)
|
||||
|
||||
sid = ps.connect('activity-appeared', self._testActivityShare_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
try:
|
||||
# Hook up to the owner's joined-activity signal
|
||||
owner = ps.get_owner()
|
||||
sid = owner.connect("joined-activity", self._testActivityShare_helper_joined_activity_cb)
|
||||
self._signals.append((owner, sid))
|
||||
except RuntimeError, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
# Wait 5 seconds max for signal to be emitted
|
||||
sid = gobject.timeout_add(5000, self._testActivityShare_helper_timeout)
|
||||
self._sources.append(sid)
|
||||
|
||||
ps.share_activity(mockact, self._AA_PROPS)
|
||||
|
||||
return False
|
||||
|
||||
def testActivityShare(self):
|
||||
ps = get_ps()
|
||||
assert ps, "Couldn't get presence service"
|
||||
|
||||
self._activity = None
|
||||
self._got_act_appeared = False
|
||||
self._joined_activity_buddy = None
|
||||
self._joined_activity_activity = None
|
||||
self._got_joined_activity = False
|
||||
gobject.idle_add(self._testActivityShare_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful."
|
||||
assert self._activity, "Shared activity was not received"
|
||||
|
||||
assert self._activity.props.id == self._AA_ID, "ID doesn't match expected"
|
||||
assert self._activity.props.name == self._AA_NAME, "Name doesn't match expected"
|
||||
# Shared activities from local machine take the owner's color
|
||||
assert self._activity.props.color == mockps._OWNER_COLOR, "Color doesn't match expected"
|
||||
assert self._activity.props.type == self._AA_TYPE, "Type doesn't match expected"
|
||||
assert self._activity.props.joined == False, "Joined doesn't match expected"
|
||||
|
||||
buddies = self._activity.get_joined_buddies()
|
||||
assert len(buddies) == 1, "No buddies in activity"
|
||||
owner = buddies[0]
|
||||
assert owner.props.key == mockps._OWNER_PUBKEY, "Buddy key doesn't match expected"
|
||||
assert owner.props.nick == mockps._OWNER_NICK, "Buddy nick doesn't match expected"
|
||||
assert owner.props.color == mockps._OWNER_COLOR, "Buddy color doesn't match expected"
|
||||
|
||||
real_owner = ps.get_owner()
|
||||
assert real_owner == owner, "Owner mismatch"
|
||||
|
||||
assert self._joined_activity_activity == self._activity, "Activity mismatch"
|
||||
assert self._joined_activity_buddy == owner, "Owner mismatch"
|
||||
|
||||
def _testActivityJoin_helper_is_done(self):
|
||||
if self._got_act_appeared and self._got_joined_activity and \
|
||||
self._got_buddy_joined:
|
||||
self._handle_success()
|
||||
|
||||
def _testActivityJoin_helper_timeout(self):
|
||||
self._handle_error("Timeout waiting for activity share")
|
||||
return False
|
||||
|
||||
def _testActivityJoin_helper_buddy_joined_cb(self, activity, buddy):
|
||||
self._buddy_joined_buddy = buddy
|
||||
self._buddy_joined_activity = activity
|
||||
self._got_buddy_joined = True
|
||||
self._testActivityJoin_helper_is_done()
|
||||
|
||||
def _testActivityJoin_helper_joined_activity_cb(self, buddy, activity):
|
||||
self._joined_activity_buddy = buddy
|
||||
self._joined_activity_activity = activity
|
||||
self._got_joined_activity = True
|
||||
self._testActivityJoin_helper_is_done()
|
||||
|
||||
def _testActivityJoin_helper_cb(self, ps, activity):
|
||||
self._activity = activity
|
||||
self._got_act_appeared = True
|
||||
|
||||
# Hook up to the join signals
|
||||
sid = activity.connect("buddy-joined", self._testActivityJoin_helper_buddy_joined_cb)
|
||||
self._signals.append((activity, sid))
|
||||
|
||||
ps = get_ps()
|
||||
owner = ps.get_owner()
|
||||
sid = owner.connect("joined-activity", self._testActivityJoin_helper_joined_activity_cb)
|
||||
self._signals.append((owner, sid))
|
||||
|
||||
# Join the activity
|
||||
activity.join()
|
||||
|
||||
def _testActivityJoin_helper(self):
|
||||
busobj = dbus.SessionBus().get_object(mockps._PRESENCE_SERVICE,
|
||||
mockps._PRESENCE_PATH)
|
||||
try:
|
||||
testps = dbus.Interface(busobj, mockps._PRESENCE_TEST_INTERFACE)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
ps = get_ps()
|
||||
sid = ps.connect('activity-appeared', self._testActivityJoin_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
|
||||
# Add a fake activity
|
||||
try:
|
||||
testps.AddActivity(self._AA_ID, self._AA_NAME, self._AA_COLOR, self._AA_TYPE, {})
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
# Wait 5 seconds max for signal to be emitted
|
||||
sid = gobject.timeout_add(5000, self._testActivityJoin_helper_timeout)
|
||||
self._sources.append(sid)
|
||||
|
||||
return False
|
||||
|
||||
def testActivityJoin(self):
|
||||
ps = get_ps()
|
||||
assert ps, "Couldn't get presence service"
|
||||
|
||||
self._activity = None
|
||||
self._got_act_appeared = False
|
||||
self._joined_activity_buddy = None
|
||||
self._joined_activity_activity = None
|
||||
self._got_joined_activity = False
|
||||
self._buddy_joined_buddy = None
|
||||
self._buddy_joined_activity = None
|
||||
self._got_buddy_joined = False
|
||||
gobject.idle_add(self._testActivityJoin_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful"
|
||||
assert self._activity, "Shared activity was not received"
|
||||
|
||||
assert self._activity.props.id == self._AA_ID, "ID doesn't match expected"
|
||||
assert self._activity.props.name == self._AA_NAME, "Name doesn't match expected"
|
||||
|
||||
buddies = self._activity.get_joined_buddies()
|
||||
assert len(buddies) == 1, "No buddies in activity"
|
||||
owner = buddies[0]
|
||||
assert owner.props.key == mockps._OWNER_PUBKEY, "Buddy key doesn't match expected"
|
||||
assert owner.props.nick == mockps._OWNER_NICK, "Buddy nick doesn't match expected"
|
||||
assert owner.props.color == mockps._OWNER_COLOR, "Buddy color doesn't match expected"
|
||||
|
||||
real_owner = ps.get_owner()
|
||||
assert real_owner == owner, "Owner mismatch"
|
||||
|
||||
assert self._joined_activity_activity == self._activity, "Activity mismatch"
|
||||
assert self._joined_activity_buddy == owner, "Owner mismatch"
|
||||
assert self._buddy_joined_activity == self._activity, "Activity mismatch"
|
||||
assert self._buddy_joined_buddy == owner, "Owner mismatch"
|
||||
|
||||
def _testCurrentActivity_helper_timeout(self):
|
||||
self._handle_error("Timeout waiting for current activity")
|
||||
return False
|
||||
|
||||
def _testCurrentActivity_set_current_activity(self, actid):
|
||||
busobj = dbus.SessionBus().get_object(mockps._PRESENCE_SERVICE,
|
||||
mockps._PRESENCE_PATH)
|
||||
try:
|
||||
testps = dbus.Interface(busobj, mockps._PRESENCE_TEST_INTERFACE)
|
||||
testps.SetBuddyCurrentActivity(self._buddy.props.key, actid)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return
|
||||
|
||||
def _testCurrentActivity_buddy_property_changed_cb(self, buddy, proplist):
|
||||
if not self._start_monitor:
|
||||
return
|
||||
if not 'current-activity' in proplist:
|
||||
return
|
||||
buddy_curact = buddy.props.current_activity
|
||||
if buddy_curact.props.id == self._AA_ID:
|
||||
self._got_first_curact = True
|
||||
# set next current activity
|
||||
self._testCurrentActivity_set_current_activity(self._other_actid)
|
||||
elif buddy_curact.props.id == self._other_actid:
|
||||
self._got_other_curact = True
|
||||
|
||||
if self._got_first_curact and self._got_other_curact:
|
||||
self._handle_success()
|
||||
|
||||
def _testCurrentActivity_start_monitor_helper(self):
|
||||
if len(self._activities) != 2 or not self._buddy:
|
||||
return
|
||||
self._start_monitor = True
|
||||
# Set first current activity
|
||||
self._testCurrentActivity_set_current_activity(self._AA_ID)
|
||||
|
||||
def _testCurrentActivity_activity_helper_cb(self, ps, activity):
|
||||
if activity in self._activities:
|
||||
self._handle_error("Activity %s already known." % activity.props.id)
|
||||
self._activities.append(activity)
|
||||
self._testCurrentActivity_start_monitor_helper()
|
||||
|
||||
def _testCurrentActivity_buddy_helper_cb(self, ps, buddy):
|
||||
self._buddy = buddy
|
||||
sid = buddy.connect("property-changed", self._testCurrentActivity_buddy_property_changed_cb)
|
||||
self._signals.append((buddy, sid))
|
||||
self._testCurrentActivity_start_monitor_helper()
|
||||
|
||||
def _testCurrentActivity_helper(self):
|
||||
busobj = dbus.SessionBus().get_object(mockps._PRESENCE_SERVICE,
|
||||
mockps._PRESENCE_PATH)
|
||||
try:
|
||||
testps = dbus.Interface(busobj, mockps._PRESENCE_TEST_INTERFACE)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
ps = get_ps()
|
||||
sid = ps.connect('activity-appeared', self._testCurrentActivity_activity_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
sid = ps.connect('buddy-appeared', self._testCurrentActivity_buddy_helper_cb)
|
||||
self._signals.append((ps, sid))
|
||||
|
||||
# Add a fake buddy
|
||||
try:
|
||||
testps.AddBuddy(BuddyTests._BA_PUBKEY, BuddyTests._BA_NICK, BuddyTests._BA_COLOR)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
# Add first fake activity
|
||||
try:
|
||||
testps.AddActivity(self._AA_ID, self._AA_NAME, self._AA_COLOR, self._AA_TYPE, {})
|
||||
testps.AddBuddyToActivity(BuddyTests._BA_PUBKEY, self._AA_ID)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
# Add second fake activity
|
||||
try:
|
||||
testps.AddActivity(self._other_actid, self._other_actname,
|
||||
self._other_actcolor, self._AA_TYPE, {})
|
||||
testps.AddBuddyToActivity(BuddyTests._BA_PUBKEY, self._other_actid)
|
||||
except dbus.exceptions.DBusException, err:
|
||||
self._handle_error(err)
|
||||
return False
|
||||
|
||||
# Wait 10 seconds max for everything to complete
|
||||
sid = gobject.timeout_add(10000, self._testCurrentActivity_helper_timeout)
|
||||
self._sources.append(sid)
|
||||
|
||||
return False
|
||||
|
||||
def testCurrentActivity(self):
|
||||
ps = get_ps()
|
||||
assert ps, "Couldn't get presence service"
|
||||
|
||||
self._other_actid = "ea8a94522c53a6741e141adece1711e4d9884678"
|
||||
self._other_actname = "Some random activity"
|
||||
self._other_actcolor = "#073838,#3A6E3A"
|
||||
self._activities = []
|
||||
self._got_first_curact = False
|
||||
self._got_other_curact = False
|
||||
self._start_monitor = False
|
||||
gobject.idle_add(self._testCurrentActivity_helper)
|
||||
gtk.main()
|
||||
|
||||
assert self._success == True, "Test unsuccessful"
|
||||
assert len(self._activities) == 2, "Shared activities were not received"
|
||||
assert self._got_first_curact == True, "Couldn't discover first activity"
|
||||
assert self._got_other_curact == True, "Couldn't discover second activity"
|
||||
assert self._start_monitor == True, "Couldn't discover both activities"
|
||||
|
||||
# check the buddy
|
||||
assert self._buddy.props.key == BuddyTests._BA_PUBKEY, "Buddy key doesn't match expected"
|
||||
assert self._buddy.props.nick == BuddyTests._BA_NICK, "Buddy nick doesn't match expected"
|
||||
assert self._buddy.props.color == BuddyTests._BA_COLOR, "Buddy color doesn't match expected"
|
||||
assert self._buddy.props.current_activity.props.id == self._other_actid, "Buddy current activity didn't match expected"
|
||||
|
||||
# check both activities
|
||||
found = 0
|
||||
for act in self._activities:
|
||||
if act.props.id == self._AA_ID:
|
||||
assert act.props.name == self._AA_NAME, "Name doesn't match expected"
|
||||
assert act.props.color == self._AA_COLOR, "Color doesn't match expected"
|
||||
buddies = act.get_joined_buddies()
|
||||
assert len(buddies) == 1, "Unexpected number of buddies in first activity"
|
||||
assert buddies[0] == self._buddy, "Unexpected buddy in first activity"
|
||||
found += 1
|
||||
elif act.props.id == self._other_actid:
|
||||
assert act.props.name == self._other_actname, "Name doesn't match expected"
|
||||
assert act.props.color == self._other_actcolor, "Color doesn't match expected"
|
||||
buddies = act.get_joined_buddies()
|
||||
assert len(buddies) == 1, "Unexpected number of buddies in first activity"
|
||||
assert buddies[0] == self._buddy, "Unexpected buddy in first activity"
|
||||
found += 1
|
||||
|
||||
assert found == 2, "Couldn't discover both activities"
|
||||
|
||||
def addToSuite(suite):
|
||||
suite.addTest(ActivityTests("testActivityAppeared"))
|
||||
suite.addTest(ActivityTests("testActivityDisappeared"))
|
||||
suite.addTest(ActivityTests("testActivityShare"))
|
||||
suite.addTest(ActivityTests("testActivityJoin"))
|
||||
suite.addTest(ActivityTests("testCurrentActivity"))
|
||||
addToSuite = staticmethod(addToSuite)
|
||||
|
||||
def main():
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
BuddyTests.addToSuite(suite)
|
||||
ActivityTests.addToSuite(suite)
|
||||
runner = unittest.TextTestRunner()
|
||||
runner.run(suite)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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
|
||||
|
||||
from sugar import objects
|
||||
|
||||
print 'MIME type for test.pdf (from extension):'
|
||||
print objects.mime.get_from_file_name('test.pdf')
|
||||
|
||||
print ''
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
print 'MIME type for file %s:' % sys.argv[1]
|
||||
print objects.mime.get_for_file(sys.argv[1])
|
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com)
|
||||
#
|
||||
# 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 pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
|
||||
from sugar.graphics.notebook import Notebook
|
||||
|
||||
window = gtk.Window()
|
||||
window.connect("destroy", lambda w: gtk.main_quit())
|
||||
window.set_size_request(800, 600)
|
||||
window.show_all()
|
||||
|
||||
nb = Notebook(can_close_tabs=True)
|
||||
|
||||
window.add(nb)
|
||||
|
||||
button1 = gtk.Button('Example 1')
|
||||
button2 = gtk.Button('Example 2')
|
||||
button3 = gtk.Button('Example 3')
|
||||
|
||||
nb.add_page('Testing label 1', button1)
|
||||
nb.add_page('Testing label 2', button2)
|
||||
nb.add_page('Testing label 3', button3)
|
||||
|
||||
gtk.main()
|
@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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 gtk
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.xocolor import XoColor
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from sugar import env
|
||||
|
||||
sys.path.append(env.get_shell_path())
|
||||
|
||||
from view.home.snowflakelayout import SnowflakeLayout
|
||||
|
||||
def add_snowflake(parent, size):
|
||||
box = hippo.CanvasBox()
|
||||
parent.append(box)
|
||||
|
||||
layout = SnowflakeLayout()
|
||||
box.set_layout(layout)
|
||||
|
||||
icon = CanvasIcon(scale=0.8, xo_color=XoColor(),
|
||||
icon_name='theme:object-link')
|
||||
layout.add_center(icon)
|
||||
|
||||
for k in range(0, size):
|
||||
icon = CanvasIcon(scale=0.4, xo_color=XoColor(),
|
||||
icon_name='theme:stock-buddy')
|
||||
layout.add(icon)
|
||||
|
||||
window = gtk.Window()
|
||||
window.set_default_size(gtk.gdk.screen_width(), gtk.gdk.screen_height())
|
||||
window.connect("destroy", lambda w: gtk.main_quit())
|
||||
window.show()
|
||||
|
||||
canvas = hippo.Canvas()
|
||||
|
||||
root = hippo.CanvasBox(background_color=0xe2e2e2ff)
|
||||
canvas.set_root(root)
|
||||
|
||||
add_snowflake(root, 10)
|
||||
add_snowflake(root, 20)
|
||||
add_snowflake(root, 15)
|
||||
add_snowflake(root, 5)
|
||||
|
||||
canvas.show()
|
||||
window.add(canvas)
|
||||
|
||||
gtk.main()
|
@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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 random
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gobject
|
||||
|
||||
import gtk
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.spreadlayout import SpreadLayout
|
||||
from sugar.graphics.xocolor import XoColor
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
|
||||
def _create_icon():
|
||||
color = XoColor()
|
||||
|
||||
scale = 1.0 + random.random() * 1.5
|
||||
icon = CanvasIcon(scale=scale, xo_color=color,
|
||||
icon_name='theme:stock-buddy')
|
||||
icon.set_tooltip('Test')
|
||||
layout.add(icon)
|
||||
|
||||
return (len(box.get_children()) < 50)
|
||||
|
||||
window = gtk.Window()
|
||||
window.connect("destroy", lambda w: gtk.main_quit())
|
||||
window.show()
|
||||
|
||||
canvas = hippo.Canvas()
|
||||
|
||||
box = hippo.CanvasBox(background_color=0xe2e2e2ff)
|
||||
|
||||
layout = SpreadLayout()
|
||||
box.set_layout(layout)
|
||||
|
||||
canvas.set_root(box)
|
||||
|
||||
window.add(canvas)
|
||||
canvas.show()
|
||||
|
||||
gobject.timeout_add(200, _create_icon)
|
||||
|
||||
gtk.main()
|
109
tests/test-ui.py
109
tests/test-ui.py
@ -1,109 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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
|
||||
|
||||
from sugar.graphics.window import Window
|
||||
from sugar.graphics.toolbutton import ToolButton
|
||||
from sugar.graphics.toolbox import Toolbox
|
||||
from sugar.graphics.palette import Palette
|
||||
|
||||
class EditToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
|
||||
class TextToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
|
||||
button = ToolButton('text-format-bold')
|
||||
self.insert(button, -1)
|
||||
button.show()
|
||||
|
||||
palette = Palette()
|
||||
button.set_palette(palette)
|
||||
|
||||
palette.set_primary_state('This is a palette')
|
||||
menu_item = gtk.MenuItem('First menu item')
|
||||
palette.append_menu_item(menu_item)
|
||||
menu_item = gtk.MenuItem('Second menu item')
|
||||
palette.append_menu_item(menu_item)
|
||||
menu_item = gtk.MenuItem('Third menu item')
|
||||
palette.append_menu_item(menu_item)
|
||||
|
||||
class ImageToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
|
||||
class TableToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
|
||||
class FormatToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
|
||||
class ViewToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
|
||||
window = Window()
|
||||
window.connect("destroy", lambda w: gtk.main_quit())
|
||||
|
||||
toolbox = Toolbox()
|
||||
window.set_toolbox(toolbox)
|
||||
toolbox.show()
|
||||
|
||||
edit_toolbar = EditToolbar()
|
||||
toolbox.add_toolbar('Edit', edit_toolbar)
|
||||
edit_toolbar.show()
|
||||
|
||||
text_toolbar = TextToolbar()
|
||||
toolbox.add_toolbar('Text', text_toolbar)
|
||||
text_toolbar.show()
|
||||
|
||||
image_toolbar = ImageToolbar()
|
||||
toolbox.add_toolbar('Image', image_toolbar)
|
||||
image_toolbar.show()
|
||||
|
||||
table_toolbar = TableToolbar()
|
||||
toolbox.add_toolbar('Table', table_toolbar)
|
||||
table_toolbar.show()
|
||||
|
||||
format_toolbar = FormatToolbar()
|
||||
toolbox.add_toolbar('Format', format_toolbar)
|
||||
format_toolbar.show()
|
||||
|
||||
view_toolbar = ViewToolbar()
|
||||
toolbox.add_toolbar('View', view_toolbar)
|
||||
view_toolbar.show()
|
||||
|
||||
toolbox.set_current_toolbar(1)
|
||||
|
||||
scrolled_window = gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
||||
window.set_canvas(scrolled_window)
|
||||
scrolled_window.show()
|
||||
|
||||
text_view = gtk.TextView()
|
||||
scrolled_window.add(text_view)
|
||||
text_view.show()
|
||||
|
||||
window.show()
|
||||
|
||||
gtk.main()
|
Loading…
Reference in New Issue
Block a user