Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn (J5) Palmieri <quinticent@localhost.localdomain>2007-08-20 18:41:03 (GMT)
committer John (J5) Palmieri <quinticent@localhost.localdomain>2007-08-20 18:41:03 (GMT)
commit655d7f41741d90617aa341db7542153261c9f6e7 (patch)
tree88c0df40a8bb109500672136fb7916b34348275b
parent735b0cf2c8a5c85b89a0b82d4fc1f40ec6ab5d12 (diff)
parent1a65f81d10186d2f82aafb17533e6b5946761ec8 (diff)
Merge branch 'master' of git+ssh://j5@dev.laptop.org/git/sugar
-rw-r--r--NEWS7
-rw-r--r--lib/ui/Makefile.am2
-rw-r--r--lib/ui/sexy-icon-entry.c984
-rw-r--r--lib/ui/sexy-icon-entry.h104
-rw-r--r--po/mk.po121
-rwxr-xr-xservices/console/console.py2
-rw-r--r--services/shell/bundleregistry.py38
-rw-r--r--services/shell/clipboardobject.py12
-rw-r--r--services/shell/clipboardservice.py8
-rw-r--r--services/shell/objecttypeservice.py12
-rw-r--r--shell/intro/colorpicker.py2
-rw-r--r--shell/model/homeactivity.py2
-rw-r--r--shell/model/homemodel.py12
-rw-r--r--shell/view/BuddyIcon.py2
-rw-r--r--shell/view/BuddyMenu.py4
-rw-r--r--shell/view/clipboardicon.py2
-rw-r--r--shell/view/clipboardmenu.py69
-rw-r--r--shell/view/devices/battery.py2
-rw-r--r--shell/view/devices/network/mesh.py2
-rw-r--r--shell/view/devices/network/wired.py2
-rw-r--r--shell/view/devices/network/wireless.py2
-rw-r--r--shell/view/frame/FriendsBox.py11
-rw-r--r--shell/view/frame/clipboardbox.py4
-rw-r--r--shell/view/frame/clipboardpanelwindow.py4
-rw-r--r--shell/view/frame/frame.py25
-rw-r--r--shell/view/frame/frameinvoker.py2
-rw-r--r--shell/view/frame/framewindow.py69
-rw-r--r--shell/view/home/HomeBox.py22
-rw-r--r--shell/view/home/HomeWindow.py27
-rw-r--r--shell/view/home/MeshBox.py4
-rw-r--r--shell/view/home/MyIcon.py2
-rw-r--r--shell/view/home/activitiesdonut.py7
-rw-r--r--shell/view/keyhandler.py4
-rwxr-xr-xsugar-emulator16
-rw-r--r--sugar/_sugaruiext.defs75
-rw-r--r--sugar/_sugaruiext.override2
-rw-r--r--sugar/_sugaruiextmodule.c2
-rw-r--r--sugar/activity/activity.py22
-rw-r--r--sugar/activity/activityservice.py2
-rw-r--r--sugar/activity/bundlebuilder.py56
-rw-r--r--sugar/clipboard/clipboardservice.py10
-rw-r--r--sugar/datastore/datastore.py3
-rw-r--r--sugar/datastore/dbus_helpers.py38
-rw-r--r--sugar/date.py14
-rw-r--r--sugar/graphics/Makefile.am2
-rw-r--r--sugar/graphics/canvasbutton.py2
-rw-r--r--sugar/graphics/canvasicon.py18
-rw-r--r--sugar/graphics/icon.py27
-rw-r--r--sugar/graphics/iconentry.py45
-rw-r--r--sugar/graphics/menuitem.py2
-rw-r--r--sugar/graphics/objectchooser.py2
-rw-r--r--sugar/graphics/palette.py94
-rw-r--r--sugar/graphics/palettegroup.py17
-rw-r--r--sugar/graphics/radiotoolbutton.py18
-rw-r--r--sugar/graphics/style.py2
-rw-r--r--sugar/graphics/toggletoolbutton.py18
-rw-r--r--sugar/graphics/toolbutton.py18
-rw-r--r--sugar/logger.py6
-rw-r--r--sugar/objects/mime.py38
-rw-r--r--sugar/presence/Makefile.am1
-rw-r--r--sugar/presence/tubeconn.py107
-rw-r--r--tests/graphics/common.py55
-rw-r--r--tests/graphics/ticket2855.py59
-rw-r--r--tests/lib/runall.py30
-rw-r--r--tests/lib/test_date.py30
-rw-r--r--tests/lib/test_mime.py72
-rwxr-xr-xtests/presence/mock-share-read.py136
-rwxr-xr-xtests/presence/mockps.py422
-rwxr-xr-xtests/presence/test-ps-bindings.py723
-rwxr-xr-xtests/test-mime.py30
-rwxr-xr-xtests/test-notebook.py42
-rwxr-xr-xtests/test-snowflake-layout.py66
-rwxr-xr-xtests/test-spread-layout.py62
-rwxr-xr-xtests/test-ui.py109
74 files changed, 2170 insertions, 1895 deletions
diff --git a/NEWS b/NEWS
index 0068c95..ecfc924 100644
--- a/NEWS
+++ b/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
diff --git a/lib/ui/Makefile.am b/lib/ui/Makefile.am
index bae36c6..5c432be 100644
--- a/lib/ui/Makefile.am
+++ b/lib/ui/Makefile.am
@@ -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 \
diff --git a/lib/ui/sexy-icon-entry.c b/lib/ui/sexy-icon-entry.c
new file mode 100644
index 0000000..ca35209
--- /dev/null
+++ b/lib/ui/sexy-icon-entry.c
@@ -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;
+}
+
diff --git a/lib/ui/sexy-icon-entry.h b/lib/ui/sexy-icon-entry.h
new file mode 100644
index 0000000..eb83fed
--- /dev/null
+++ b/lib/ui/sexy-icon-entry.h
@@ -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_ */
diff --git a/po/mk.po b/po/mk.po
index 7dbefec..99173f6 100644
--- a/po/mk.po
+++ b/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 активност"
diff --git a/services/console/console.py b/services/console/console.py
index dae34c3..4fb3609 100755
--- a/services/console/console.py
+++ b/services/console/console.py
@@ -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()
diff --git a/services/shell/bundleregistry.py b/services/shell/bundleregistry.py
index 65a2348..19d0032 100644
--- a/services/shell/bundleregistry.py
+++ b/services/shell/bundleregistry.py
@@ -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
diff --git a/services/shell/clipboardobject.py b/services/shell/clipboardobject.py
index bc51f47..c3cdab3 100644
--- a/services/shell/clipboardobject.py
+++ b/services/shell/clipboardobject.py
@@ -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
diff --git a/services/shell/clipboardservice.py b/services/shell/clipboardservice.py
index 19958a7..90e1b8e 100644
--- a/services/shell/clipboardservice.py
+++ b/services/shell/clipboardservice.py
@@ -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)
diff --git a/services/shell/objecttypeservice.py b/services/shell/objecttypeservice.py
index e12398e..f949375 100644
--- a/services/shell/objecttypeservice.py
+++ b/services/shell/objecttypeservice.py
@@ -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,
diff --git a/shell/intro/colorpicker.py b/shell/intro/colorpicker.py
index f7ab59c..d70d84c 100644
--- a/shell/intro/colorpicker.py
+++ b/shell/intro/colorpicker.py
@@ -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)
diff --git a/shell/model/homeactivity.py b/shell/model/homeactivity.py
index e95fe9a..434a3a6 100644
--- a/shell/model/homeactivity.py
+++ b/shell/model/homeactivity.py
@@ -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
diff --git a/shell/model/homemodel.py b/shell/model/homemodel.py
index dac434a..759c2b8 100644
--- a/shell/model/homemodel.py
+++ b/shell/model/homemodel.py
@@ -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)
diff --git a/shell/view/BuddyIcon.py b/shell/view/BuddyIcon.py
index ebd12f0..f16b71c 100644
--- a/shell/view/BuddyIcon.py
+++ b/shell/view/BuddyIcon.py
@@ -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
diff --git a/shell/view/BuddyMenu.py b/shell/view/BuddyMenu.py
index e3efb5c..e3d1a20 100644
--- a/shell/view/BuddyMenu.py
+++ b/shell/view/BuddyMenu.py
@@ -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)
diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py
index 2e60e36..c9d910a 100644
--- a/shell/view/clipboardicon.py
+++ b/shell/view/clipboardicon.py
@@ -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
diff --git a/shell/view/clipboardmenu.py b/shell/view/clipboardmenu.py
index 28ea0bb..0875f70 100644
--- a/shell/view/clipboardmenu.py
+++ b/shell/view/clipboardmenu.py
@@ -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()
diff --git a/shell/view/devices/battery.py b/shell/view/devices/battery.py
index 16863d3..cdd2e78 100644
--- a/shell/view/devices/battery.py
+++ b/shell/view/devices/battery.py
@@ -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
diff --git a/shell/view/devices/network/mesh.py b/shell/view/devices/network/mesh.py
index 1f108cb..10830f2 100644
--- a/shell/view/devices/network/mesh.py
+++ b/shell/view/devices/network/mesh.py
@@ -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)
diff --git a/shell/view/devices/network/wired.py b/shell/view/devices/network/wired.py
index e2b9ce3..662c672 100644
--- a/shell/view/devices/network/wired.py
+++ b/shell/view/devices/network/wired.py
@@ -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'
diff --git a/shell/view/devices/network/wireless.py b/shell/view/devices/network/wireless.py
index 0be096d..9f0abd8 100644
--- a/shell/view/devices/network/wireless.py
+++ b/shell/view/devices/network/wireless.py
@@ -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):
diff --git a/shell/view/frame/FriendsBox.py b/shell/view/frame/FriendsBox.py
index 562c042..30f5963 100644
--- a/shell/view/frame/FriendsBox.py
+++ b/shell/view/frame/FriendsBox.py
@@ -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):
diff --git a/shell/view/frame/clipboardbox.py b/shell/view/frame/clipboardbox.py
index 60c154b..33d583c 100644
--- a/shell/view/frame/clipboardbox.py
+++ b/shell/view/frame/clipboardbox.py
@@ -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':
diff --git a/shell/view/frame/clipboardpanelwindow.py b/shell/view/frame/clipboardpanelwindow.py
index 70b87e4..217096a 100644
--- a/shell/view/frame/clipboardpanelwindow.py
+++ b/shell/view/frame/clipboardpanelwindow.py
@@ -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)
diff --git a/shell/view/frame/frame.py b/shell/view/frame/frame.py
index 1c4680e..2582fe6 100644
--- a/shell/view/frame/frame.py
+++ b/shell/view/frame/frame.py
@@ -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
diff --git a/shell/view/frame/frameinvoker.py b/shell/view/frame/frameinvoker.py
index 1b99d50..8be0eaf 100644
--- a/shell/view/frame/frameinvoker.py
+++ b/shell/view/frame/frameinvoker.py
@@ -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
diff --git a/shell/view/frame/framewindow.py b/shell/view/frame/framewindow.py
index 469ea65..6738bf2 100644
--- a/shell/view/frame/framewindow.py
+++ b/shell/view/frame/framewindow.py
@@ -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)
diff --git a/shell/view/home/HomeBox.py b/shell/view/home/HomeBox.py
index 2fa2183..f340c1a 100644
--- a/shell/view/home/HomeBox.py
+++ b/shell/view/home/HomeBox.py
@@ -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()
diff --git a/shell/view/home/HomeWindow.py b/shell/view/home/HomeWindow.py
index ce8fe74..2f7040e 100644
--- a/shell/view/home/HomeWindow.py
+++ b/shell/view/home/HomeWindow.py
@@ -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
diff --git a/shell/view/home/MeshBox.py b/shell/view/home/MeshBox.py
index e632770..ff6d290 100644
--- a/shell/view/home/MeshBox.py
+++ b/shell/view/home/MeshBox.py
@@ -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):
diff --git a/shell/view/home/MyIcon.py b/shell/view/home/MyIcon.py
index 2168b0b..f4013b9 100644
--- a/shell/view/home/MyIcon.py
+++ b/shell/view/home/MyIcon.py
@@ -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())
diff --git a/shell/view/home/activitiesdonut.py b/shell/view/home/activitiesdonut.py
index 3b12a09..306cdf5 100644
--- a/shell/view/home/activitiesdonut.py
+++ b/shell/view/home/activitiesdonut.py
@@ -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')
diff --git a/shell/view/keyhandler.py b/shell/view/keyhandler.py
index 1d5c481..23075d4 100644
--- a/shell/view/keyhandler.py
+++ b/shell/view/keyhandler.py
@@ -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()
diff --git a/sugar-emulator b/sugar-emulator
index 6ab9292..3a96a8a 100755
--- a/sugar-emulator
+++ b/sugar-emulator
@@ -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__":
diff --git a/sugar/_sugaruiext.defs b/sugar/_sugaruiext.defs
index 3c011e1..6a9129d 100644
--- a/sugar/_sugaruiext.defs
+++ b/sugar/_sugaruiext.defs
@@ -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")
+)
+
diff --git a/sugar/_sugaruiext.override b/sugar/_sugaruiext.override
index 6daafc3..beeaad0 100644
--- a/sugar/_sugaruiext.override
+++ b/sugar/_sugaruiext.override
@@ -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
diff --git a/sugar/_sugaruiextmodule.c b/sugar/_sugaruiextmodule.c
index 8c74010..719b153 100644
--- a/sugar/_sugaruiextmodule.c
+++ b/sugar/_sugaruiextmodule.c
@@ -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");
diff --git a/sugar/activity/activity.py b/sugar/activity/activity.py
index 262d89b..553b1a1 100644
--- a/sugar/activity/activity.py
+++ b/sugar/activity/activity.py
@@ -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
diff --git a/sugar/activity/activityservice.py b/sugar/activity/activityservice.py
index c9ee482..f2b3394 100644
--- a/sugar/activity/activityservice.py
+++ b/sugar/activity/activityservice.py
@@ -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
diff --git a/sugar/activity/bundlebuilder.py b/sugar/activity/bundlebuilder.py
index 3bbe454..b0e46ab 100644
--- a/sugar/activity/bundlebuilder.py
+++ b/sugar/activity/bundlebuilder.py
@@ -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':
diff --git a/sugar/clipboard/clipboardservice.py b/sugar/clipboard/clipboardservice.py
index dbdf41d..0e357fe 100644
--- a/sugar/clipboard/clipboardservice.py
+++ b/sugar/clipboard/clipboardservice.py
@@ -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),)
diff --git a/sugar/datastore/datastore.py b/sugar/datastore/datastore.py
index 5ba994d..0dbe35b 100644
--- a/sugar/datastore/datastore.py
+++ b/sugar/datastore/datastore.py
@@ -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()
diff --git a/sugar/datastore/dbus_helpers.py b/sugar/datastore/dbus_helpers.py
index f0cfa3b..442a35d 100644
--- a/sugar/datastore/dbus_helpers.py
+++ b/sugar/datastore/dbus_helpers.py
@@ -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'))
diff --git a/sugar/date.py b/sugar/date.py
index 3b68818..3f4dcc2 100644
--- a/sugar/date.py
+++ b/sugar/date.py
@@ -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')
diff --git a/sugar/graphics/Makefile.am b/sugar/graphics/Makefile.am
index af66cfb..2d0dc17 100644
--- a/sugar/graphics/Makefile.am
+++ b/sugar/graphics/Makefile.am
@@ -9,7 +9,7 @@ sugar_PYTHON = \
combobox.py \
icon.py \
iconbutton.py \
- menuitem.py \
+ iconentry.py \
notebook.py \
objectchooser.py \
radiotoolbutton.py \
diff --git a/sugar/graphics/canvasbutton.py b/sugar/graphics/canvasbutton.py
index 31bc833..fc869f9 100644
--- a/sugar/graphics/canvasbutton.py
+++ b/sugar/graphics/canvasbutton.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()
diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py
index 39f1358..a77eecd 100644
--- a/sugar/graphics/canvasicon.py
+++ b/sugar/graphics/canvasicon.py
@@ -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]
diff --git a/sugar/graphics/icon.py b/sugar/graphics/icon.py
index 731412f..e324d45 100644
--- a/sugar/graphics/icon.py
+++ b/sugar/graphics/icon.py
@@ -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)
diff --git a/sugar/graphics/iconentry.py b/sugar/graphics/iconentry.py
new file mode 100644
index 0000000..2f7584f
--- /dev/null
+++ b/sugar/graphics/iconentry.py
@@ -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)
+
diff --git a/sugar/graphics/menuitem.py b/sugar/graphics/menuitem.py
index 492f5f1..db4a293 100644
--- a/sugar/graphics/menuitem.py
+++ b/sugar/graphics/menuitem.py
@@ -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()
diff --git a/sugar/graphics/objectchooser.py b/sugar/graphics/objectchooser.py
index 5b09e13..46739f6 100644
--- a/sugar/graphics/objectchooser.py
+++ b/sugar/graphics/objectchooser.py
@@ -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
diff --git a/sugar/graphics/palette.py b/sugar/graphics/palette.py
index 368a0f6..6b1209a 100644
--- a/sugar/graphics/palette.py
+++ b/sugar/graphics/palette.py
@@ -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):
+ def has_rectangle_gap(self):
+ return True
+
+ def draw_rectangle(self, event, palette):
style = self._widget.style
- if palette.is_up():
- 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,
- gap[0], gap[1], gap[2])
- else:
- style.paint_box(event.window, gtk.STATE_PRELIGHT,
+ 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)
diff --git a/sugar/graphics/palettegroup.py b/sugar/graphics/palettegroup.py
index d5e1125..d6ae122 100644
--- a/sugar/graphics/palettegroup.py
+++ b/sugar/graphics/palettegroup.py
@@ -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')
diff --git a/sugar/graphics/radiotoolbutton.py b/sugar/graphics/radiotoolbutton.py
index fb584ee..4ffc7d1 100644
--- a/sugar/graphics/radiotoolbutton.py
+++ b/sugar/graphics/radiotoolbutton.py
@@ -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)
diff --git a/sugar/graphics/style.py b/sugar/graphics/style.py
index c0094b6..9d561eb 100644
--- a/sugar/graphics/style.py
+++ b/sugar/graphics/style.py
@@ -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')
diff --git a/sugar/graphics/toggletoolbutton.py b/sugar/graphics/toggletoolbutton.py
index 41050e2..74e78a6 100644
--- a/sugar/graphics/toggletoolbutton.py
+++ b/sugar/graphics/toggletoolbutton.py
@@ -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)
diff --git a/sugar/graphics/toolbutton.py b/sugar/graphics/toolbutton.py
index 52a5d62..b42f63e 100644
--- a/sugar/graphics/toolbutton.py
+++ b/sugar/graphics/toolbutton.py
@@ -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)
diff --git a/sugar/logger.py b/sugar/logger.py
index e857044..72c4a99 100644
--- a/sugar/logger.py
+++ b/sugar/logger.py
@@ -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)
diff --git a/sugar/objects/mime.py b/sugar/objects/mime.py
index 80eac9b..b933143 100644
--- a/sugar/objects/mime.py
+++ b/sugar/objects/mime.py
@@ -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
diff --git a/sugar/presence/Makefile.am b/sugar/presence/Makefile.am
index 6314c1a..cb52a41 100644
--- a/sugar/presence/Makefile.am
+++ b/sugar/presence/Makefile.am
@@ -3,5 +3,6 @@ sugar_PYTHON = \
__init__.py \
activity.py \
buddy.py \
+ tubeconn.py \
presenceservice.py
diff --git a/sugar/presence/tubeconn.py b/sugar/presence/tubeconn.py
new file mode 100644
index 0000000..d1c1403
--- /dev/null
+++ b/sugar/presence/tubeconn.py
@@ -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, [])
diff --git a/tests/graphics/common.py b/tests/graphics/common.py
new file mode 100644
index 0000000..2f00099
--- /dev/null
+++ b/tests/graphics/common.py
@@ -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()
diff --git a/tests/graphics/ticket2855.py b/tests/graphics/ticket2855.py
new file mode 100644
index 0000000..f680e95
--- /dev/null
+++ b/tests/graphics/ticket2855.py
@@ -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)
diff --git a/tests/lib/runall.py b/tests/lib/runall.py
new file mode 100644
index 0000000..6ee9442
--- /dev/null
+++ b/tests/lib/runall.py
@@ -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)
diff --git a/tests/lib/test_date.py b/tests/lib/test_date.py
new file mode 100644
index 0000000..c6da871
--- /dev/null
+++ b/tests/lib/test_date.py
@@ -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()
diff --git a/tests/lib/test_mime.py b/tests/lib/test_mime.py
new file mode 100644
index 0000000..88598fe
--- /dev/null
+++ b/tests/lib/test_mime.py
@@ -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()
+
diff --git a/tests/presence/mock-share-read.py b/tests/presence/mock-share-read.py
deleted file mode 100755
index 80be169..0000000
--- a/tests/presence/mock-share-read.py
+++ /dev/null
@@ -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()
diff --git a/tests/presence/mockps.py b/tests/presence/mockps.py
deleted file mode 100755
index 3468515..0000000
--- a/tests/presence/mockps.py
+++ /dev/null
@@ -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()
diff --git a/tests/presence/test-ps-bindings.py b/tests/presence/test-ps-bindings.py
deleted file mode 100755
index ccf6617..0000000
--- a/tests/presence/test-ps-bindings.py
+++ /dev/null
@@ -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()
diff --git a/tests/test-mime.py b/tests/test-mime.py
deleted file mode 100755
index a7f185e..0000000
--- a/tests/test-mime.py
+++ /dev/null
@@ -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])
diff --git a/tests/test-notebook.py b/tests/test-notebook.py
deleted file mode 100755
index 262b38e..0000000
--- a/tests/test-notebook.py
+++ /dev/null
@@ -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()
diff --git a/tests/test-snowflake-layout.py b/tests/test-snowflake-layout.py
deleted file mode 100755
index 279f638..0000000
--- a/tests/test-snowflake-layout.py
+++ /dev/null
@@ -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()
diff --git a/tests/test-spread-layout.py b/tests/test-spread-layout.py
deleted file mode 100755
index 412d7fc..0000000
--- a/tests/test-spread-layout.py
+++ /dev/null
@@ -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()
diff --git a/tests/test-ui.py b/tests/test-ui.py
deleted file mode 100755
index bb80201..0000000
--- a/tests/test-ui.py
+++ /dev/null
@@ -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()