Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
This commit is contained in:
commit
7368429ad6
2
.gitignore
vendored
2
.gitignore
vendored
@ -38,6 +38,7 @@ mkinstalldirs
|
|||||||
po/Makefile.in.in
|
po/Makefile.in.in
|
||||||
po/POTFILES
|
po/POTFILES
|
||||||
po/*.gmo
|
po/*.gmo
|
||||||
|
po/.intltool-merge-cache
|
||||||
sugar/__installed__.py
|
sugar/__installed__.py
|
||||||
tools/sugar-setup-activity
|
tools/sugar-setup-activity
|
||||||
threadframe
|
threadframe
|
||||||
@ -53,4 +54,5 @@ browser/sugar-marshal.h
|
|||||||
bin/sugar
|
bin/sugar
|
||||||
shell/extensions/_extensions.c
|
shell/extensions/_extensions.c
|
||||||
data/sugar.gtkrc
|
data/sugar.gtkrc
|
||||||
|
data/sugar.xml
|
||||||
data/sugar-xo.gtkrc
|
data/sugar-xo.gtkrc
|
||||||
|
1
NEWS
1
NEWS
@ -1,3 +1,4 @@
|
|||||||
|
* Draw an invoker that is connected with the palette for toolbuttons. (benzea)
|
||||||
* Fix traceback when reading in saved WPA2 network configs (dcbw)
|
* Fix traceback when reading in saved WPA2 network configs (dcbw)
|
||||||
* #2475 Retrieve correctly the file path for files in removable devices. (tomeu)
|
* #2475 Retrieve correctly the file path for files in removable devices. (tomeu)
|
||||||
* #2119 If config is missing start intro. (marco)
|
* #2119 If config is missing start intro. (marco)
|
||||||
|
10
configure.ac
10
configure.ac
@ -21,8 +21,10 @@ PKG_CHECK_MODULES(SHELL, pygtk-2.0 gtk+-2.0)
|
|||||||
|
|
||||||
PKG_CHECK_MODULES(NATIVE_FACTORY, dbus-1)
|
PKG_CHECK_MODULES(NATIVE_FACTORY, dbus-1)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(LIB, gtk+-2.0)
|
PKG_CHECK_MODULES(LIBUI, gtk+-2.0)
|
||||||
PKG_CHECK_MODULES(LIB_BINDINGS, pygtk-2.0)
|
PKG_CHECK_MODULES(LIBUI_BINDINGS, pygtk-2.0)
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES(LIB_BINDINGS, pygobject-2.0)
|
||||||
|
|
||||||
PYGTK_DEFSDIR=`$PKG_CONFIG --variable=defsdir pygtk-2.0`
|
PYGTK_DEFSDIR=`$PKG_CONFIG --variable=defsdir pygtk-2.0`
|
||||||
AC_SUBST(PYGTK_DEFSDIR)
|
AC_SUBST(PYGTK_DEFSDIR)
|
||||||
@ -45,7 +47,7 @@ data/Makefile
|
|||||||
lib/Makefile
|
lib/Makefile
|
||||||
lib/ui/Makefile
|
lib/ui/Makefile
|
||||||
services/Makefile
|
services/Makefile
|
||||||
services/clipboard/Makefile
|
services/shell/Makefile
|
||||||
shell/Makefile
|
shell/Makefile
|
||||||
shell/intro/Makefile
|
shell/intro/Makefile
|
||||||
shell/hardware/Makefile
|
shell/hardware/Makefile
|
||||||
@ -64,7 +66,7 @@ services/console/Makefile
|
|||||||
services/console/interface/Makefile
|
services/console/interface/Makefile
|
||||||
services/console/interface/xo/Makefile
|
services/console/interface/xo/Makefile
|
||||||
services/console/interface/memphis/plugins/clean_size/Makefile
|
services/console/interface/memphis/plugins/clean_size/Makefile
|
||||||
services/console/interface/memphis/plugins/dirty_size/Makefile
|
services/console/interface/memphis/plugins/smaps/Makefile
|
||||||
services/console/interface/memphis/plugins/Makefile
|
services/console/interface/memphis/plugins/Makefile
|
||||||
services/console/interface/memphis/plugins/memphis_init/Makefile
|
services/console/interface/memphis/plugins/memphis_init/Makefile
|
||||||
services/console/interface/memphis/plugins/cpu/Makefile
|
services/console/interface/memphis/plugins/cpu/Makefile
|
||||||
|
@ -14,5 +14,23 @@ GTKRC_FILES = \
|
|||||||
sugar.gtkrc \
|
sugar.gtkrc \
|
||||||
sugar-xo.gtkrc
|
sugar-xo.gtkrc
|
||||||
|
|
||||||
EXTRA_DIST = $(sugar_DATA) em.py gtkrc.em
|
|
||||||
|
mime_xml_in_files = sugar.xml.in
|
||||||
|
mime_xml_files = $(mime_xml_in_files:.xml.in=.xml)
|
||||||
|
@INTLTOOL_XML_RULE@
|
||||||
|
|
||||||
|
mimedir = $(datadir)/mime/packages
|
||||||
|
mime_DATA = $(mime_xml_files)
|
||||||
|
|
||||||
|
install-data-hook:
|
||||||
|
if [ -z "$$DESTDIR" ]; then \
|
||||||
|
update-mime-database "$(datadir)/mime"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
if [ -z "$$DESTDIR" ]; then \
|
||||||
|
update-mime-database "$(datadir)/mime"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
EXTRA_DIST = $(sugar_DATA) $(mime_xml_in_files) em.py gtkrc.em
|
||||||
CLEANFILES = $(GTKRC_FILES)
|
CLEANFILES = $(GTKRC_FILES)
|
||||||
|
7
data/sugar.xml.in
Normal file
7
data/sugar.xml.in
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||||
|
<mime-type type="application/vnd.olpc-x-sugar">
|
||||||
|
<_comment>Sugar activity bundle</_comment>
|
||||||
|
<glob pattern="*.xo"/>
|
||||||
|
</mime-type>
|
||||||
|
</mime-info>
|
@ -1,10 +1,10 @@
|
|||||||
libsugarui_la_CPPFLAGS = \
|
libsugarui_la_CPPFLAGS = \
|
||||||
$(LIB_CFLAGS)
|
$(LIBUI_CFLAGS)
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libsugarui.la
|
noinst_LTLIBRARIES = libsugarui.la
|
||||||
|
|
||||||
libsugarui_la_LIBADD = \
|
libsugarui_la_LIBADD = \
|
||||||
$(LIB_LIBS)
|
$(LIBUI_LIBS)
|
||||||
|
|
||||||
libsugarui_la_SOURCES = \
|
libsugarui_la_SOURCES = \
|
||||||
$(BUILT_SOURCES) \
|
$(BUILT_SOURCES) \
|
||||||
|
@ -28,57 +28,36 @@ static void sugar_menu_init (SugarMenu *menu);
|
|||||||
G_DEFINE_TYPE(SugarMenu, sugar_menu, GTK_TYPE_MENU)
|
G_DEFINE_TYPE(SugarMenu, sugar_menu, GTK_TYPE_MENU)
|
||||||
|
|
||||||
void
|
void
|
||||||
sugar_menu_popup(SugarMenu *menu,
|
sugar_menu_set_active(SugarMenu *menu, gboolean active)
|
||||||
int x,
|
|
||||||
int y)
|
|
||||||
{
|
{
|
||||||
GtkWidget *window;
|
GTK_MENU_SHELL(menu)->active = active;
|
||||||
|
|
||||||
window = GTK_MENU(menu)->toplevel;
|
|
||||||
g_return_if_fail(window != NULL);
|
|
||||||
|
|
||||||
GTK_MENU_SHELL(menu)->active = TRUE;
|
|
||||||
|
|
||||||
gtk_widget_show(GTK_WIDGET(menu));
|
|
||||||
|
|
||||||
gtk_window_move(GTK_WINDOW(window), x, y);
|
|
||||||
gtk_widget_show(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sugar_menu_popdown(SugarMenu *menu)
|
sugar_menu_embed(SugarMenu *menu, GtkContainer *parent)
|
||||||
{
|
{
|
||||||
GtkWidget *window;
|
menu->orig_toplevel = GTK_MENU(menu)->toplevel;
|
||||||
|
|
||||||
window = GTK_MENU(menu)->toplevel;
|
GTK_MENU(menu)->toplevel = gtk_widget_get_toplevel(GTK_WIDGET(parent));
|
||||||
g_return_if_fail(window != NULL);
|
gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(parent));
|
||||||
|
|
||||||
GTK_MENU_SHELL(menu)->active = FALSE;
|
|
||||||
|
|
||||||
gtk_widget_hide(GTK_WIDGET(menu));
|
|
||||||
gtk_widget_hide(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
sugar_menu_size_request (GtkWidget *widget,
|
sugar_menu_unembed(SugarMenu *menu)
|
||||||
GtkRequisition *requisition)
|
|
||||||
{
|
{
|
||||||
SugarMenu *menu = SUGAR_MENU(widget);
|
if (menu->orig_toplevel) {
|
||||||
|
GTK_MENU(menu)->toplevel = menu->orig_toplevel;
|
||||||
(* GTK_WIDGET_CLASS (sugar_menu_parent_class)->size_request) (widget, requisition);
|
gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(menu->orig_toplevel));
|
||||||
|
}
|
||||||
requisition->width = MAX(requisition->width, menu->min_width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sugar_menu_class_init(SugarMenuClass *menu_class)
|
sugar_menu_class_init(SugarMenuClass *menu_class)
|
||||||
{
|
{
|
||||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(menu_class);
|
|
||||||
|
|
||||||
widget_class->size_request = sugar_menu_size_request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sugar_menu_init(SugarMenu *menu)
|
sugar_menu_init(SugarMenu *menu)
|
||||||
{
|
{
|
||||||
|
menu->orig_toplevel = NULL;
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,9 @@ typedef struct _SugarMenuClass SugarMenuClass;
|
|||||||
#define SUGAR_MENU_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), SUGAR_TYPE_MENU, SugarMenuClass))
|
#define SUGAR_MENU_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), SUGAR_TYPE_MENU, SugarMenuClass))
|
||||||
|
|
||||||
struct _SugarMenu {
|
struct _SugarMenu {
|
||||||
GtkMenu base_instance;
|
GtkMenu base_instance;
|
||||||
|
|
||||||
|
GtkWidget *orig_toplevel;
|
||||||
int min_width;
|
int min_width;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,13 +45,11 @@ struct _SugarMenuClass {
|
|||||||
GtkMenuClass base_class;
|
GtkMenuClass base_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType sugar_menu_get_type (void);
|
GType sugar_menu_get_type (void);
|
||||||
void sugar_menu_popup (SugarMenu *menu,
|
void sugar_menu_set_active (SugarMenu *menu,
|
||||||
int x,
|
gboolean active);
|
||||||
int y);
|
void sugar_menu_embed (SugarMenu *menu,
|
||||||
void sugar_menu_set_min_width (SugarMenu *menu,
|
GtkContainer *parent);
|
||||||
int min_width);
|
|
||||||
void sugar_menu_popdown (SugarMenu *menu);
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
SUBDIRS = clipboard console
|
SUBDIRS = shell console
|
||||||
|
@ -28,29 +28,29 @@ import pango
|
|||||||
from sugar import env
|
from sugar import env
|
||||||
|
|
||||||
class MultiLogView(gtk.VBox):
|
class MultiLogView(gtk.VBox):
|
||||||
def __init__(self, path):
|
def __init__(self, path, extra_files):
|
||||||
self._active_log = None
|
self._active_log = None
|
||||||
self._iters = []
|
self._extra_files = extra_files
|
||||||
|
|
||||||
# Creating Main treeview with Actitivities list
|
# Creating Main treeview with Actitivities list
|
||||||
tv_menu = gtk.TreeView()
|
self._tv_menu = gtk.TreeView()
|
||||||
tv_menu.connect('cursor-changed', self._load_log)
|
self._tv_menu.connect('cursor-changed', self._load_log)
|
||||||
tv_menu.set_rules_hint(True)
|
self._tv_menu.set_rules_hint(True)
|
||||||
|
|
||||||
# Set width
|
# Set width
|
||||||
box_width = gtk.gdk.screen_width() * 80 / 100
|
box_width = gtk.gdk.screen_width() * 80 / 100
|
||||||
tv_menu.set_size_request(box_width*25/100, 0)
|
self._tv_menu.set_size_request(box_width*25/100, 0)
|
||||||
|
|
||||||
self.store_menu = gtk.TreeStore(str)
|
self._store_menu = gtk.TreeStore(str)
|
||||||
tv_menu.set_model(self.store_menu)
|
self._tv_menu.set_model(self._store_menu)
|
||||||
|
|
||||||
self._add_column(tv_menu, 'Sugar logs', 0)
|
self._add_column(self._tv_menu, 'Sugar logs', 0)
|
||||||
self._logs_path = os.path.join(env.get_profile_path(), 'logs')
|
self._logs_path = os.path.join(env.get_profile_path(), 'logs')
|
||||||
self._activity = {}
|
self._activity = {}
|
||||||
|
|
||||||
# Activities menu
|
# Activities menu
|
||||||
self.hbox = gtk.HBox(False, 3)
|
self.hbox = gtk.HBox(False, 3)
|
||||||
self.hbox.pack_start(tv_menu, True, True, 0)
|
self.hbox.pack_start(self._tv_menu, True, True, 0)
|
||||||
|
|
||||||
# Activity log, set width
|
# Activity log, set width
|
||||||
self._view = LogView()
|
self._view = LogView()
|
||||||
@ -59,52 +59,62 @@ class MultiLogView(gtk.VBox):
|
|||||||
self.hbox.pack_start(self._view, True, True, 0)
|
self.hbox.pack_start(self._view, True, True, 0)
|
||||||
self.hbox.show_all()
|
self.hbox.show_all()
|
||||||
|
|
||||||
gobject.timeout_add(1000, self._update, tv_menu)
|
gobject.timeout_add(1000, self._update)
|
||||||
|
|
||||||
# Load the log information in View (textview)
|
# Load the log information in View (textview)
|
||||||
def _load_log(self, treeview):
|
def _load_log(self, treeview):
|
||||||
treeselection = treeview.get_selection()
|
treeselection = treeview.get_selection()
|
||||||
|
|
||||||
treestore, iter = treeselection.get_selected()
|
treestore, iter = treeselection.get_selected()
|
||||||
|
|
||||||
# Get current selection
|
# Get current selection
|
||||||
act_log = self.store_menu.get_value(iter, 0)
|
act_log = self._store_menu.get_value(iter, 0)
|
||||||
|
|
||||||
# Set buffer and scroll down
|
# Set buffer and scroll down
|
||||||
self._view.textview.set_buffer(self._activity[act_log])
|
self._view.textview.set_buffer(self._activity[act_log])
|
||||||
self._view.textview.scroll_to_mark(self._activity[act_log].get_insert(), 0);
|
self._view.textview.scroll_to_mark(self._activity[act_log].get_insert(), 0);
|
||||||
self._active_log = act_log
|
self._active_log = act_log
|
||||||
|
|
||||||
def _update(self, tv_menu):
|
def _update(self):
|
||||||
# Searching log files
|
# Searching log files
|
||||||
for logfile in os.listdir(self._logs_path):
|
for logfile in os.listdir(self._logs_path):
|
||||||
full_log_path = os.path.join(self._logs_path, logfile)
|
full_log_path = os.path.join(self._logs_path, logfile)
|
||||||
|
self._add_log_file(full_log_path)
|
||||||
|
|
||||||
if os.path.isdir(full_log_path):
|
for ext in self._extra_files:
|
||||||
continue
|
self._add_log_file(ext)
|
||||||
|
|
||||||
if not self._activity.has_key(logfile):
|
|
||||||
self._add_activity(logfile)
|
|
||||||
model = LogBuffer(full_log_path)
|
|
||||||
self._activity[logfile] = model
|
|
||||||
|
|
||||||
self._activity[logfile].update()
|
|
||||||
written = self._activity[logfile]._written
|
|
||||||
|
|
||||||
# Load the first iter
|
|
||||||
if self._active_log == None:
|
|
||||||
self._active_log = logfile
|
|
||||||
iter = tv_menu.get_model().get_iter_root()
|
|
||||||
tv_menu.get_selection().select_iter(iter)
|
|
||||||
self._load_log(tv_menu)
|
|
||||||
|
|
||||||
if written > 0 and self._active_log == logfile:
|
|
||||||
self._view.textview.scroll_to_mark(self._activity[logfile].get_insert(), 0);
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _get_filename_from_path(self, path):
|
||||||
|
return path.split('/')[-1]
|
||||||
|
|
||||||
|
def _add_log_file(self, path):
|
||||||
|
if os.path.isdir(path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
logfile = self._get_filename_from_path(path)
|
||||||
|
|
||||||
|
if not self._activity.has_key(logfile):
|
||||||
|
self._add_activity(logfile)
|
||||||
|
model = LogBuffer(path)
|
||||||
|
self._activity[logfile] = model
|
||||||
|
|
||||||
|
self._activity[logfile].update()
|
||||||
|
written = self._activity[logfile]._written
|
||||||
|
|
||||||
|
# Load the first iter
|
||||||
|
if self._active_log == None:
|
||||||
|
self._active_log = logfile
|
||||||
|
iter = self._tv_menu.get_model().get_iter_root()
|
||||||
|
self._tv_menu.get_selection().select_iter(iter)
|
||||||
|
self._load_log(self._tv_menu)
|
||||||
|
|
||||||
|
if written > 0 and self._active_log == logfile:
|
||||||
|
self._view.textview.scroll_to_mark(self._activity[logfile].get_insert(), 0)
|
||||||
|
|
||||||
|
|
||||||
def _add_activity(self, name):
|
def _add_activity(self, name):
|
||||||
self._insert_row(self.store_menu, None, name)
|
self._insert_row(self._store_menu, None, name)
|
||||||
|
|
||||||
# Add a new column to the main treeview, (code from Memphis)
|
# Add a new column to the main treeview, (code from Memphis)
|
||||||
def _add_column(self, treeview, column_name, index):
|
def _add_column(self, treeview, column_name, index):
|
||||||
@ -171,9 +181,20 @@ class LogView(gtk.ScrolledWindow):
|
|||||||
self.textview.show()
|
self.textview.show()
|
||||||
|
|
||||||
class Interface:
|
class Interface:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
path = None
|
path = None
|
||||||
viewer = MultiLogView(path)
|
xserver_logfile = self._get_xserver_logfile_path()
|
||||||
|
|
||||||
|
# Aditional files to watch in logviewer
|
||||||
|
ext_files = []
|
||||||
|
ext_files.append(xserver_logfile)
|
||||||
|
|
||||||
|
viewer = MultiLogView(path, ext_files)
|
||||||
self.widget = viewer.hbox
|
self.widget = viewer.hbox
|
||||||
|
|
||||||
|
# Get the Xorg log file path, we have two ways to get the path: do a system
|
||||||
|
# call and exec a 'xset -q' or just read directly the file that we know
|
||||||
|
# always be the right one for a XO machine...
|
||||||
|
def _get_xserver_logfile_path(self):
|
||||||
|
path = "/var/log/Xorg.0.log"
|
||||||
|
return path
|
||||||
|
@ -130,15 +130,14 @@ class Data:
|
|||||||
treeview.set_model(self.store)
|
treeview.set_model(self.store)
|
||||||
|
|
||||||
def _start_memphis(self, button):
|
def _start_memphis(self, button):
|
||||||
|
|
||||||
# Update information every 1.5 second
|
# Update information every 1.5 second
|
||||||
button.hide()
|
button.hide()
|
||||||
self.interface.button_stop.show()
|
self.interface.button_stop.show()
|
||||||
self._running_status = True
|
self._running_status = True
|
||||||
gobject.timeout_add(1500, self.load_data, self.treeview)
|
self._gid = gobject.timeout_add(1500, self.load_data, self.treeview)
|
||||||
|
|
||||||
def _stop_memphis(self, button):
|
def _stop_memphis(self, button):
|
||||||
|
gobject.source_remove(self._gid)
|
||||||
self._running_status = False
|
self._running_status = False
|
||||||
button.hide()
|
button.hide()
|
||||||
self.interface.button_start.show()
|
self.interface.button_start.show()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
SUBDIRS = clean_size cpu dirty_size memphis_init
|
SUBDIRS = clean_size cpu smaps memphis_init
|
||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins
|
sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins
|
||||||
sugar_PYTHON =
|
sugar_PYTHON =
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
import info
|
|
||||||
|
|
||||||
|
|
||||||
INTERNALS = {
|
|
||||||
# Basic information
|
|
||||||
'PLGNAME': "Dirty Size",
|
|
||||||
'TABNAME': None, # No tabbed plugin
|
|
||||||
'AUTHOR': "Eduardo Silva",
|
|
||||||
'DESC': "Get dirty size memory usage",
|
|
||||||
|
|
||||||
# Plugin API
|
|
||||||
'Plg': None, # Plugin object
|
|
||||||
|
|
||||||
'top_data': [int], # Top data types needed by memphis core plugin
|
|
||||||
'top_cols': ["PDRSS (kb)"]
|
|
||||||
}
|
|
@ -11,3 +11,4 @@ def plg_on_top_data_refresh(self, ppinfo):
|
|||||||
data = [ppinfo['pid'], ppinfo['name'], ppinfo['state_name']]
|
data = [ppinfo['pid'], ppinfo['name'], ppinfo['state_name']]
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
17
services/console/interface/memphis/plugins/smaps/__init__.py
Normal file
17
services/console/interface/memphis/plugins/smaps/__init__.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
import info
|
||||||
|
|
||||||
|
|
||||||
|
INTERNALS = {
|
||||||
|
# Basic information
|
||||||
|
'PLGNAME': "SMaps",
|
||||||
|
'TABNAME': None, # No tabbed plugin
|
||||||
|
'AUTHOR': "Eduardo Silva",
|
||||||
|
'DESC': "Get dirty size and reference memory usage",
|
||||||
|
|
||||||
|
# Plugin API
|
||||||
|
'Plg': None, # Plugin object
|
||||||
|
|
||||||
|
'top_data': [int, int], # Top data types needed by memphis core plugin
|
||||||
|
'top_cols': ["PDRSS (kb)", "Referenced (kb)"]
|
||||||
|
}
|
@ -8,13 +8,12 @@
|
|||||||
|
|
||||||
|
|
||||||
def plg_on_top_data_refresh(self, ppinfo):
|
def plg_on_top_data_refresh(self, ppinfo):
|
||||||
|
smaps = get_data(self, ppinfo['pid'])
|
||||||
|
|
||||||
dirty_sizes = get_dirty(self, ppinfo['pid'])
|
# memphis need an array
|
||||||
|
return [smaps['private_dirty'], smaps['referenced']]
|
||||||
|
|
||||||
# memhis need an array
|
def get_data(pself, pid):
|
||||||
return [dirty_sizes['private']]
|
|
||||||
|
|
||||||
def get_dirty(pself, pid):
|
|
||||||
ProcAnalysis = pself.INTERNALS['Plg'].proc_analysis(pid)
|
ProcAnalysis = pself.INTERNALS['Plg'].proc_analysis(pid)
|
||||||
|
|
||||||
return ProcAnalysis.DirtyRSS()
|
return ProcAnalysis.SMaps()
|
@ -32,7 +32,7 @@ class Terminal(gtk.HBox):
|
|||||||
|
|
||||||
self._vte = vte.Terminal()
|
self._vte = vte.Terminal()
|
||||||
self._configure_vte()
|
self._configure_vte()
|
||||||
self._vte.set_size(30, 5)
|
self._vte.set_size(100, 5)
|
||||||
self._vte.set_size_request(200, 450)
|
self._vte.set_size_request(200, 450)
|
||||||
self._vte.show()
|
self._vte.show()
|
||||||
self.pack_start(self._vte)
|
self.pack_start(self._vte)
|
||||||
|
@ -7,20 +7,22 @@ class Analysis:
|
|||||||
def __init__(self, pid):
|
def __init__(self, pid):
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
|
|
||||||
def DirtyRSS(self):
|
def SMaps(self):
|
||||||
smaps = proc_smaps.ProcSmaps(self.pid)
|
smaps = proc_smaps.ProcSmaps(self.pid)
|
||||||
dirty = []
|
private_dirty = 0
|
||||||
|
shared_dirty = 0
|
||||||
private = 0
|
referenced = 0
|
||||||
shared = 0
|
|
||||||
|
|
||||||
for map in smaps.mappings:
|
for map in smaps.mappings:
|
||||||
private += map.private_dirty
|
private_dirty += map.private_dirty
|
||||||
shared += map.shared_dirty
|
shared_dirty += map.shared_dirty
|
||||||
|
referenced += map.referenced
|
||||||
|
|
||||||
dirty = {"private": int(private), "shared": int(shared)}
|
smaps = {"private_dirty": int(private_dirty), \
|
||||||
|
"shared_dirty": int(shared_dirty),\
|
||||||
|
"referenced": int(referenced)}
|
||||||
|
|
||||||
return dirty
|
return smaps
|
||||||
|
|
||||||
def ApproxRealMemoryUsage(self):
|
def ApproxRealMemoryUsage(self):
|
||||||
maps = proc_smaps.ProcMaps(self.pid)
|
maps = proc_smaps.ProcMaps(self.pid)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import sys, os
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
import string
|
import string
|
||||||
|
|
||||||
class ProcInfo:
|
class ProcInfo:
|
||||||
@ -36,10 +38,12 @@ class ProcInfo:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# Parsing data , check 'man 5 proc' for details
|
# Parsing data , check 'man 5 proc' for details
|
||||||
data = infile.read().split()
|
stat_data = infile.read()
|
||||||
|
|
||||||
infile.close()
|
infile.close()
|
||||||
|
|
||||||
|
process_name = self._get_process_name(stat_data)
|
||||||
|
data = self._get_safe_split(stat_data)
|
||||||
|
|
||||||
state_dic = {
|
state_dic = {
|
||||||
'R': 'Running',
|
'R': 'Running',
|
||||||
'S': 'Sleeping',
|
'S': 'Sleeping',
|
||||||
@ -51,24 +55,31 @@ class ProcInfo:
|
|||||||
|
|
||||||
# user and group owners
|
# user and group owners
|
||||||
pidstat = os.stat(pidfile)
|
pidstat = os.stat(pidfile)
|
||||||
|
|
||||||
info = {
|
info = {
|
||||||
'pid': int(data[0]), # Process ID
|
'pid': int(data[0]), # Process ID
|
||||||
'name': data[1].strip('()'), # Process name
|
'name': process_name,
|
||||||
'state': data[2], # Process State, ex: R|S|D|Z|T|W
|
'state': data[2], # Process State, ex: R|S|D|Z|T|W
|
||||||
'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc
|
'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc
|
||||||
'ppid': int(data[3]), # Parent process ID
|
'ppid': int(data[3]), # Parent process ID
|
||||||
'utime': int(data[13]), # Used jiffies in user mode
|
'utime': int(data[13]), # Used jiffies in user mode
|
||||||
'stime': int(data[14]), # Used jiffies in kernel mode
|
'stime': int(data[14]), # Used jiffies in kernel mode
|
||||||
'start_time': int(data[21]), # Process time from system boot (jiffies)
|
'start_time': int(data[21]), # Process time from system boot (jiffies)
|
||||||
'vsize': int(data[22]), # Virtual memory size used (bytes)
|
'vsize': int(data[22]), # Virtual memory size used (bytes)
|
||||||
'rss': int(data[23])*4, # Resident Set Size (bytes)
|
'rss': int(data[23])*4, # Resident Set Size (bytes)
|
||||||
'user_id': pidstat.st_uid, # process owner
|
'user_id': pidstat.st_uid, # process owner
|
||||||
'group_id': pidstat.st_gid # owner group
|
'group_id': pidstat.st_gid # owner group
|
||||||
}
|
}
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
# Return the process name
|
||||||
|
def _get_process_name(self, data):
|
||||||
|
m = re.search("\(.*\)", data)
|
||||||
|
return m.string[m.start()+1:m.end()-1]
|
||||||
|
|
||||||
|
def _get_safe_split(self, data):
|
||||||
|
new_data = re.sub("\(.*\)", '()', data)
|
||||||
|
return new_data.split()
|
||||||
|
|
||||||
# Returns the CPU usage expressed in Jiffies
|
# Returns the CPU usage expressed in Jiffies
|
||||||
def get_CPU_usage(self, cpu_hz, used_jiffies, start_time):
|
def get_CPU_usage(self, cpu_hz, used_jiffies, start_time):
|
||||||
|
@ -36,6 +36,7 @@ class ProcSmaps:
|
|||||||
# Shared_Dirty: 0 kB
|
# Shared_Dirty: 0 kB
|
||||||
# Private_Clean: 8 kB
|
# Private_Clean: 8 kB
|
||||||
# Private_Dirty: 0 kB
|
# Private_Dirty: 0 kB
|
||||||
|
# Referenced: 4 kb -> Introduced in kernel 2.6.22
|
||||||
|
|
||||||
while num_lines > 0:
|
while num_lines > 0:
|
||||||
fields = lines[line_idx].split (" ", 5)
|
fields = lines[line_idx].split (" ", 5)
|
||||||
@ -51,13 +52,20 @@ class ProcSmaps:
|
|||||||
shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4])
|
shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4])
|
||||||
private_clean = self.parse_smaps_size_line (lines[line_idx + 5])
|
private_clean = self.parse_smaps_size_line (lines[line_idx + 5])
|
||||||
private_dirty = self.parse_smaps_size_line (lines[line_idx + 6])
|
private_dirty = self.parse_smaps_size_line (lines[line_idx + 6])
|
||||||
|
referenced = self.parse_smaps_size_line (lines[line_idx + 7])
|
||||||
name = name.strip ()
|
name = name.strip ()
|
||||||
|
|
||||||
mapping = Mapping (size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name)
|
mapping = Mapping (size, rss, shared_clean, shared_dirty, \
|
||||||
|
private_clean, private_dirty, referenced, permissions, name)
|
||||||
self.mappings.append (mapping)
|
self.mappings.append (mapping)
|
||||||
|
|
||||||
num_lines -= 7
|
num_lines -= 8
|
||||||
line_idx += 7
|
line_idx += 8
|
||||||
|
|
||||||
|
self._clear_reference(pid)
|
||||||
|
|
||||||
|
def _clear_reference(self, pid):
|
||||||
|
os.system("echo 1 > /proc/%s/clear_refs" % pid)
|
||||||
|
|
||||||
# Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field
|
# Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field
|
||||||
def parse_smaps_size_line (self, line):
|
def parse_smaps_size_line (self, line):
|
||||||
@ -66,13 +74,15 @@ class ProcSmaps:
|
|||||||
return int(fields[1])
|
return int(fields[1])
|
||||||
|
|
||||||
class Mapping:
|
class Mapping:
|
||||||
def __init__ (self, size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name):
|
def __init__ (self, size, rss, shared_clean, shared_dirty, \
|
||||||
|
private_clean, private_dirty, referenced, permissions, name):
|
||||||
self.size = size
|
self.size = size
|
||||||
self.rss = rss
|
self.rss = rss
|
||||||
self.shared_clean = shared_clean
|
self.shared_clean = shared_clean
|
||||||
self.shared_dirty = shared_dirty
|
self.shared_dirty = shared_dirty
|
||||||
self.private_clean = private_clean
|
self.private_clean = private_clean
|
||||||
self.private_dirty = private_dirty
|
self.private_dirty = private_dirty
|
||||||
|
self.referenced = referenced
|
||||||
self.permissions = permissions
|
self.permissions = permissions
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
@ -6,7 +6,15 @@ pygtk.require('2.0')
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from sugar import env
|
from sugar import env
|
||||||
|
from sugar import util
|
||||||
|
|
||||||
sys.path.append(env.get_service_path('console'))
|
sys.path.append(env.get_service_path('console'))
|
||||||
|
|
||||||
|
# change to the user's home directory if it is set
|
||||||
|
# root if not
|
||||||
|
os.chdir(os.environ.get('HOME', '/'))
|
||||||
|
|
||||||
|
#set the process title so it shows up as sugar-console not python
|
||||||
|
util.set_proc_title('sugar-console')
|
||||||
|
|
||||||
import console
|
import console
|
||||||
|
@ -1,29 +1,37 @@
|
|||||||
servicedir = $(datadir)/dbus-1/services
|
servicedir = $(datadir)/dbus-1/services
|
||||||
|
|
||||||
service_in_files = \
|
service_in_files = \
|
||||||
|
org.laptop.ActivityRegistry.service.in \
|
||||||
org.laptop.Clipboard.service.in \
|
org.laptop.Clipboard.service.in \
|
||||||
org.laptop.ObjectTypeRegistry.service.in
|
org.laptop.ObjectTypeRegistry.service.in
|
||||||
|
|
||||||
service_DATA = \
|
service_DATA = \
|
||||||
|
org.laptop.ActivityRegistry.service \
|
||||||
org.laptop.Clipboard.service \
|
org.laptop.Clipboard.service \
|
||||||
org.laptop.ObjectTypeRegistry.service
|
org.laptop.ObjectTypeRegistry.service
|
||||||
|
|
||||||
|
org.laptop.ActivityRegistry.service: org.laptop.ActivityRegistry.service.in Makefile
|
||||||
|
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
||||||
|
|
||||||
org.laptop.Clipboard.service: org.laptop.Clipboard.service.in Makefile
|
org.laptop.Clipboard.service: org.laptop.Clipboard.service.in Makefile
|
||||||
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
||||||
|
|
||||||
org.laptop.ObjectTypeRegistry.service: org.laptop.ObjectTypeRegistry.service.in Makefile
|
org.laptop.ObjectTypeRegistry.service: org.laptop.ObjectTypeRegistry.service.in Makefile
|
||||||
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/clipboard
|
sugardir = $(pkgdatadir)/services/shell
|
||||||
|
|
||||||
sugar_PYTHON = \
|
sugar_PYTHON = \
|
||||||
__init__.py \
|
__init__.py \
|
||||||
clipboardobject.py \
|
activityregistryservice.py \
|
||||||
clipboardservice.py \
|
bundleregistry.py \
|
||||||
|
clipboardobject.py \
|
||||||
|
clipboardservice.py \
|
||||||
objecttypeservice.py
|
objecttypeservice.py
|
||||||
|
|
||||||
bin_SCRIPTS = sugar-clipboard
|
bin_SCRIPTS = sugar-shell-service
|
||||||
|
|
||||||
DISTCLEANFILES = $(service_DATA)
|
DISTCLEANFILES = $(service_DATA)
|
||||||
|
|
||||||
EXTRA_DIST = $(service_in_files) $(bin_SCRIPTS)
|
EXTRA_DIST = $(service_in_files) $(bin_SCRIPTS)
|
||||||
|
|
114
services/shell/activityregistryservice.py
Normal file
114
services/shell/activityregistryservice.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# Copyright (C) 2006-2007 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 dbus
|
||||||
|
import dbus.service
|
||||||
|
|
||||||
|
import bundleregistry
|
||||||
|
|
||||||
|
_ACTIVITY_REGISTRY_SERVICE_NAME = 'org.laptop.ActivityRegistry'
|
||||||
|
_ACTIVITY_REGISTRY_IFACE = 'org.laptop.ActivityRegistry'
|
||||||
|
_ACTIVITY_REGISTRY_PATH = '/org/laptop/ActivityRegistry'
|
||||||
|
|
||||||
|
class ActivityRegistry(dbus.service.Object):
|
||||||
|
def __init__(self):
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
bus_name = dbus.service.BusName(_ACTIVITY_REGISTRY_SERVICE_NAME, bus=bus)
|
||||||
|
dbus.service.Object.__init__(self, bus_name, _ACTIVITY_REGISTRY_PATH)
|
||||||
|
|
||||||
|
bundle_registry = bundleregistry.get_registry()
|
||||||
|
bundle_registry.connect('bundle-added', self._bundle_added_cb)
|
||||||
|
|
||||||
|
@dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
|
||||||
|
in_signature='s', out_signature='b')
|
||||||
|
def AddBundle(self, bundle_path):
|
||||||
|
'''Register the activity bundle with the global registry
|
||||||
|
|
||||||
|
bundle_path -- path to the activity bundle's root directory,
|
||||||
|
that is, the directory with activity/activity.info as a
|
||||||
|
child of the directory.
|
||||||
|
|
||||||
|
The bundleregistry.BundleRegistry is responsible for setting
|
||||||
|
up a set of d-bus service mappings for each available activity.
|
||||||
|
'''
|
||||||
|
registry = bundleregistry.get_registry()
|
||||||
|
return registry.add_bundle(bundle_path)
|
||||||
|
|
||||||
|
@dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
|
||||||
|
in_signature='', out_signature='aa{sv}')
|
||||||
|
def GetActivities(self):
|
||||||
|
result = []
|
||||||
|
registry = bundleregistry.get_registry()
|
||||||
|
for bundle in registry:
|
||||||
|
result.append(self._bundle_to_dict(bundle))
|
||||||
|
return result
|
||||||
|
|
||||||
|
@dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
|
||||||
|
in_signature='s', out_signature='a{sv}')
|
||||||
|
def GetActivity(self, service_name):
|
||||||
|
registry = bundleregistry.get_registry()
|
||||||
|
bundle = registry.get_bundle(service_name)
|
||||||
|
if not bundle:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return self._bundle_to_dict(bundle)
|
||||||
|
|
||||||
|
@dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
|
||||||
|
in_signature='s', out_signature='aa{sv}')
|
||||||
|
def FindActivity(self, name):
|
||||||
|
result = []
|
||||||
|
key = name.lower()
|
||||||
|
|
||||||
|
for bundle in bundleregistry.get_registry():
|
||||||
|
name = bundle.get_name().lower()
|
||||||
|
service_name = bundle.get_service_name().lower()
|
||||||
|
if name.find(key) != -1 or service_name.find(key) != -1:
|
||||||
|
result.append(self._bundle_to_dict(bundle))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
|
||||||
|
in_signature='s', out_signature='aa{sv}')
|
||||||
|
def GetActivitiesForType(self, mime_type):
|
||||||
|
result = []
|
||||||
|
registry = bundleregistry.get_registry()
|
||||||
|
for bundle in registry.get_activities_for_type(mime_type):
|
||||||
|
result.append(self._bundle_to_dict(bundle))
|
||||||
|
return result
|
||||||
|
|
||||||
|
@dbus.service.signal(_ACTIVITY_REGISTRY_IFACE, signature='a{sv}')
|
||||||
|
def ActivityAdded(self, activity_info):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _bundle_to_dict(self, bundle):
|
||||||
|
return {'name': bundle.get_name(),
|
||||||
|
'icon': bundle.get_icon(),
|
||||||
|
'service_name': bundle.get_service_name(),
|
||||||
|
'path': bundle.get_path(),
|
||||||
|
'show_launcher': bundle.get_show_launcher()}
|
||||||
|
|
||||||
|
def _bundle_added_cb(self, bundle_registry, bundle):
|
||||||
|
self.ActivityAdded(self._bundle_to_dict(bundle))
|
||||||
|
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def get_instance():
|
||||||
|
global _instance
|
||||||
|
if not _instance:
|
||||||
|
_instance = ActivityRegistry()
|
||||||
|
return _instance
|
||||||
|
|
@ -106,6 +106,13 @@ class BundleRegistry(gobject.GObject):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_activities_for_type(self, mime_type):
|
||||||
|
result = []
|
||||||
|
for bundle in self._bundles.values():
|
||||||
|
if bundle.get_mime_types() and mime_type in bundle.get_mime_types():
|
||||||
|
result.append(bundle)
|
||||||
|
return result
|
||||||
|
|
||||||
def get_registry():
|
def get_registry():
|
||||||
return _bundle_registry
|
return _bundle_registry
|
||||||
|
|
@ -19,9 +19,9 @@ import logging
|
|||||||
import urlparse
|
import urlparse
|
||||||
|
|
||||||
from sugar.objects import mime
|
from sugar.objects import mime
|
||||||
from sugar import activity
|
|
||||||
|
|
||||||
import objecttypeservice
|
import objecttypeservice
|
||||||
|
import bundleregistry
|
||||||
|
|
||||||
class ClipboardObject:
|
class ClipboardObject:
|
||||||
|
|
||||||
@ -66,30 +66,15 @@ class ClipboardObject:
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
def get_activity(self):
|
def get_activity(self):
|
||||||
logging.debug('get_activity')
|
|
||||||
mapping = {'text/html' : 'org.laptop.WebActivity',
|
|
||||||
'image/jpeg' : 'org.laptop.WebActivity',
|
|
||||||
'image/gif' : 'org.laptop.WebActivity',
|
|
||||||
'image/png' : 'org.laptop.WebActivity',
|
|
||||||
'text/plain' : 'org.laptop.AbiWordActivity',
|
|
||||||
'text/rtf' : 'org.laptop.AbiWordActivity',
|
|
||||||
'text/richtext' : 'org.laptop.AbiWordActivity',
|
|
||||||
'application/pdf' : 'org.laptop.sugar.ReadActivity',
|
|
||||||
'application/x-squeak-project' : 'org.vpri.EtoysActivity'}
|
|
||||||
mime = self.get_mime_type()
|
mime = self.get_mime_type()
|
||||||
if not mime:
|
if not mime:
|
||||||
return ''
|
return ''
|
||||||
"""
|
|
||||||
registry = activity.get_registry()
|
registry = bundleregistry.get_registry()
|
||||||
activities = registry.get_activities_for_type(self.get_mime_type())
|
activities = registry.get_activities_for_type(self.get_mime_type())
|
||||||
# TODO: should we return several activities?
|
# TODO: should we return several activities?
|
||||||
if activities:
|
if activities:
|
||||||
return activities[0]
|
return activities[0].get_service_name()
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
"""
|
|
||||||
if mapping.has_key(mime):
|
|
||||||
return mapping[mime]
|
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@ -101,8 +86,6 @@ class ClipboardObject:
|
|||||||
|
|
||||||
def add_format(self, format):
|
def add_format(self, format):
|
||||||
self._formats[format.get_type()] = format
|
self._formats[format.get_type()] = format
|
||||||
# We want to get the activity early in order to prevent a DBus lockup.
|
|
||||||
activity = self.get_activity()
|
|
||||||
|
|
||||||
def get_formats(self):
|
def get_formats(self):
|
||||||
return self._formats
|
return self._formats
|
4
services/shell/org.laptop.ActivityRegistry.service.in
Normal file
4
services/shell/org.laptop.ActivityRegistry.service.in
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[D-BUS Service]
|
||||||
|
Name = org.laptop.ActivityRegistry
|
||||||
|
Exec = @bindir@/sugar-shell-service
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
[D-BUS Service]
|
[D-BUS Service]
|
||||||
Name = org.laptop.Clipboard
|
Name = org.laptop.Clipboard
|
||||||
Exec = @bindir@/sugar-clipboard
|
Exec = @bindir@/sugar-shell-service
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
[D-BUS Service]
|
[D-BUS Service]
|
||||||
Name = org.laptop.ObjectTypeRegistry
|
Name = org.laptop.ObjectTypeRegistry
|
||||||
Exec = @bindir@/sugar-clipboard
|
Exec = @bindir@/sugar-shell-service
|
||||||
|
|
@ -23,28 +23,31 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sugar import logger
|
from sugar import logger
|
||||||
logger.start('clipboard')
|
logger.start('shellservice')
|
||||||
|
|
||||||
import gobject
|
import gobject
|
||||||
import dbus.glib
|
import dbus.glib
|
||||||
|
|
||||||
from sugar import env
|
from sugar import env
|
||||||
|
|
||||||
sys.path.append(env.get_service_path('clipboard'))
|
sys.path.append(env.get_service_path('shell'))
|
||||||
|
|
||||||
import clipboardservice
|
import clipboardservice
|
||||||
import objecttypeservice
|
import objecttypeservice
|
||||||
|
import activityregistryservice
|
||||||
|
|
||||||
logging.info('Starting clipboard service.')
|
logging.info('Starting shell service.')
|
||||||
|
|
||||||
gobject.threads_init()
|
gobject.threads_init()
|
||||||
dbus.glib.threads_init()
|
dbus.glib.threads_init()
|
||||||
|
|
||||||
clipboard_service = clipboardservice.get_instance()
|
clipboard_service = clipboardservice.get_instance()
|
||||||
object_type_registry = objecttypeservice.get_instance()
|
object_type_registry = objecttypeservice.get_instance()
|
||||||
|
activity_registry = activityregistryservice.get_instance()
|
||||||
|
|
||||||
loop = gobject.MainLoop()
|
loop = gobject.MainLoop()
|
||||||
try:
|
try:
|
||||||
loop.run()
|
loop.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print 'Ctrl+C pressed, exiting...'
|
print 'Ctrl+C pressed, exiting...'
|
||||||
|
|
@ -4,7 +4,6 @@ sugardir = $(pkgdatadir)/shell/model
|
|||||||
sugar_PYTHON = \
|
sugar_PYTHON = \
|
||||||
__init__.py \
|
__init__.py \
|
||||||
accesspointmodel.py \
|
accesspointmodel.py \
|
||||||
bundleregistry.py \
|
|
||||||
BuddyModel.py \
|
BuddyModel.py \
|
||||||
Friends.py \
|
Friends.py \
|
||||||
Invites.py \
|
Invites.py \
|
||||||
|
@ -18,29 +18,29 @@ import gobject
|
|||||||
|
|
||||||
from sugar.graphics.xocolor import XoColor
|
from sugar.graphics.xocolor import XoColor
|
||||||
from sugar.presence import presenceservice
|
from sugar.presence import presenceservice
|
||||||
|
from sugar import activity
|
||||||
|
|
||||||
from model import bundleregistry
|
|
||||||
from model.BuddyModel import BuddyModel
|
from model.BuddyModel import BuddyModel
|
||||||
from model.accesspointmodel import AccessPointModel
|
from model.accesspointmodel import AccessPointModel
|
||||||
from hardware import hardwaremanager
|
from hardware import hardwaremanager
|
||||||
from hardware import nmclient
|
from hardware import nmclient
|
||||||
|
|
||||||
class ActivityModel:
|
class ActivityModel:
|
||||||
def __init__(self, activity, bundle):
|
def __init__(self, activity, activity_info):
|
||||||
self._activity = activity
|
self._activity = activity
|
||||||
self._bundle = bundle
|
self._activity_info = activity_info
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return self._activity.props.id
|
return self._activity.props.id
|
||||||
|
|
||||||
def get_icon_name(self):
|
def get_icon_name(self):
|
||||||
return self._bundle.get_icon()
|
return self._activity_info.icon
|
||||||
|
|
||||||
def get_color(self):
|
def get_color(self):
|
||||||
return XoColor(self._activity.props.color)
|
return XoColor(self._activity.props.color)
|
||||||
|
|
||||||
def get_service_name(self):
|
def get_service_name(self):
|
||||||
return self._bundle.get_service_name()
|
return self._activity_info.service_name
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
return self._activity.props.name
|
return self._activity.props.name
|
||||||
@ -75,7 +75,6 @@ class MeshModel(gobject.GObject):
|
|||||||
self._buddies = {}
|
self._buddies = {}
|
||||||
self._access_points = {}
|
self._access_points = {}
|
||||||
self._mesh = None
|
self._mesh = None
|
||||||
self._bundle_registry = bundleregistry.get_registry()
|
|
||||||
|
|
||||||
self._pservice = presenceservice.get_instance()
|
self._pservice = presenceservice.get_instance()
|
||||||
self._pservice.connect("activity-appeared",
|
self._pservice.connect("activity-appeared",
|
||||||
@ -196,13 +195,14 @@ class MeshModel(gobject.GObject):
|
|||||||
def _activity_appeared_cb(self, pservice, activity):
|
def _activity_appeared_cb(self, pservice, activity):
|
||||||
self._check_activity(activity)
|
self._check_activity(activity)
|
||||||
|
|
||||||
def _check_activity(self, activity):
|
def _check_activity(self, presence_activity):
|
||||||
bundle = self._bundle_registry.get_bundle(activity.props.type)
|
registry = activity.get_registry()
|
||||||
if not bundle:
|
activity_info = registry.get_activity(presence_activity.props.type)
|
||||||
|
if not activity_info:
|
||||||
return
|
return
|
||||||
if self.has_activity(activity.props.id):
|
if self.has_activity(presence_activity.props.id):
|
||||||
return
|
return
|
||||||
self.add_activity(bundle, activity)
|
self.add_activity(activity_info, presence_activity)
|
||||||
|
|
||||||
def has_activity(self, activity_id):
|
def has_activity(self, activity_id):
|
||||||
return self._activities.has_key(activity_id)
|
return self._activities.has_key(activity_id)
|
||||||
@ -213,8 +213,8 @@ class MeshModel(gobject.GObject):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_activity(self, bundle, activity):
|
def add_activity(self, activity_info, activity):
|
||||||
model = ActivityModel(activity, bundle)
|
model = ActivityModel(activity, activity_info)
|
||||||
self._activities[model.get_id()] = model
|
self._activities[model.get_id()] = model
|
||||||
self.emit('activity-added', model)
|
self.emit('activity-added', model)
|
||||||
|
|
||||||
|
@ -44,10 +44,10 @@ class HomeActivity(gobject.GObject):
|
|||||||
gobject.PARAM_READWRITE),
|
gobject.PARAM_READWRITE),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, bundle, activity_id):
|
def __init__(self, activity_info, activity_id):
|
||||||
"""Initialise the HomeActivity
|
"""Initialise the HomeActivity
|
||||||
|
|
||||||
bundle -- sugar.activity.bundle.Bundle instance,
|
activity_info -- sugar.activity.registry.ActivityInfo instance,
|
||||||
provides the information required to actually
|
provides the information required to actually
|
||||||
create the new instance. This is, in effect,
|
create the new instance. This is, in effect,
|
||||||
the "type" of activity being created.
|
the "type" of activity being created.
|
||||||
@ -61,7 +61,7 @@ class HomeActivity(gobject.GObject):
|
|||||||
self._pid = None
|
self._pid = None
|
||||||
self._service = None
|
self._service = None
|
||||||
self._activity_id = activity_id
|
self._activity_id = activity_id
|
||||||
self._bundle = bundle
|
self._activity_info = activity_info
|
||||||
self._launch_time = time.time()
|
self._launch_time = time.time()
|
||||||
self._launching = False
|
self._launching = False
|
||||||
|
|
||||||
@ -99,9 +99,9 @@ class HomeActivity(gobject.GObject):
|
|||||||
return self._window.get_name()
|
return self._window.get_name()
|
||||||
|
|
||||||
def get_icon_name(self):
|
def get_icon_name(self):
|
||||||
"""Retrieve the bundle's icon (file) name"""
|
"""Retrieve the activity's icon (file) name"""
|
||||||
if self._bundle:
|
if self._activity_info:
|
||||||
return self._bundle.get_icon()
|
return self._activity_info.icon
|
||||||
else:
|
else:
|
||||||
return 'theme:stock-missing'
|
return 'theme:stock-missing'
|
||||||
|
|
||||||
@ -156,9 +156,9 @@ class HomeActivity(gobject.GObject):
|
|||||||
return self._window
|
return self._window
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
"""Retrieve bundle's "service_name" for future reference"""
|
"""Retrieve activity_info's "service_name" for future reference"""
|
||||||
if self._bundle:
|
if self._activity_info:
|
||||||
return self._bundle.get_service_name()
|
return self._activity_info.service_name
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@ import wnck
|
|||||||
import dbus
|
import dbus
|
||||||
|
|
||||||
from sugar import wm
|
from sugar import wm
|
||||||
|
from sugar import activity
|
||||||
|
|
||||||
from model.homeactivity import HomeActivity
|
from model.homeactivity import HomeActivity
|
||||||
from model import bundleregistry
|
|
||||||
|
|
||||||
class HomeModel(gobject.GObject):
|
class HomeModel(gobject.GObject):
|
||||||
"""Model of the "Home" view (activity management)
|
"""Model of the "Home" view (activity management)
|
||||||
@ -51,15 +51,18 @@ class HomeModel(gobject.GObject):
|
|||||||
([gobject.TYPE_PYOBJECT])),
|
([gobject.TYPE_PYOBJECT])),
|
||||||
'active-activity-changed': (gobject.SIGNAL_RUN_FIRST,
|
'active-activity-changed': (gobject.SIGNAL_RUN_FIRST,
|
||||||
gobject.TYPE_NONE,
|
gobject.TYPE_NONE,
|
||||||
([gobject.TYPE_PYOBJECT]))
|
([gobject.TYPE_PYOBJECT])),
|
||||||
|
'pending-activity-changed': (gobject.SIGNAL_RUN_FIRST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
([gobject.TYPE_PYOBJECT]))
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
self._activities = []
|
self._activities = []
|
||||||
self._bundle_registry = bundleregistry.get_registry()
|
self._active_activity = None
|
||||||
self._current_activity = None
|
self._pending_activity = None
|
||||||
|
|
||||||
screen = wnck.screen_get_default()
|
screen = wnck.screen_get_default()
|
||||||
screen.connect('window-opened', self._window_opened_cb)
|
screen.connect('window-opened', self._window_opened_cb)
|
||||||
@ -67,8 +70,55 @@ class HomeModel(gobject.GObject):
|
|||||||
screen.connect('active-window-changed',
|
screen.connect('active-window-changed',
|
||||||
self._active_window_changed_cb)
|
self._active_window_changed_cb)
|
||||||
|
|
||||||
def get_current_activity(self):
|
def get_pending_activity(self):
|
||||||
return self._current_activity
|
"""Returns the activity that would be seen in the Activity zoom level
|
||||||
|
|
||||||
|
In the Home (or Neighborhood or Groups) zoom level, this
|
||||||
|
indicates the activity that would become active if the user
|
||||||
|
switched to the Activity zoom level. (In the Activity zoom
|
||||||
|
level, this just returns the currently-active activity.)
|
||||||
|
Unlike get_active_activity(), this never returns None as long
|
||||||
|
as there is any activity running.
|
||||||
|
"""
|
||||||
|
return self._pending_activity
|
||||||
|
|
||||||
|
def _set_pending_activity(self, home_activity):
|
||||||
|
if self._pending_activity == home_activity:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._pending_activity = home_activity
|
||||||
|
self.emit('pending-activity-changed', self._pending_activity)
|
||||||
|
|
||||||
|
def get_active_activity(self):
|
||||||
|
"""Returns the activity that the user is currently working in
|
||||||
|
|
||||||
|
In the Activity zoom level, this returns the currently-active
|
||||||
|
activity. In the other zoom levels, it returns the activity
|
||||||
|
that was most-recently active in the Activity zoom level, or
|
||||||
|
None if the most-recently-active activity is no longer
|
||||||
|
running.
|
||||||
|
"""
|
||||||
|
return self._active_activity
|
||||||
|
|
||||||
|
def _set_active_activity(self, home_activity):
|
||||||
|
if self._active_activity == home_activity:
|
||||||
|
return
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
|
||||||
|
self._active_activity = home_activity
|
||||||
|
self.emit('active-activity-changed', self._active_activity)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self._activities)
|
return iter(self._activities)
|
||||||
@ -84,65 +134,48 @@ class HomeModel(gobject.GObject):
|
|||||||
|
|
||||||
def _window_opened_cb(self, screen, window):
|
def _window_opened_cb(self, screen, window):
|
||||||
if window.get_window_type() == wnck.WINDOW_NORMAL:
|
if window.get_window_type() == wnck.WINDOW_NORMAL:
|
||||||
activity = None
|
home_activity = None
|
||||||
|
|
||||||
activity_id = wm.get_activity_id(window)
|
activity_id = wm.get_activity_id(window)
|
||||||
|
|
||||||
bundle_id = wm.get_bundle_id(window)
|
service_name = wm.get_bundle_id(window)
|
||||||
if bundle_id:
|
if service_name:
|
||||||
bundle = self._bundle_registry.get_bundle(bundle_id)
|
registry = activity.get_registry()
|
||||||
|
activity_info = registry.get_activity(service_name)
|
||||||
else:
|
else:
|
||||||
bundle = None
|
activity_info = None
|
||||||
|
|
||||||
if activity_id:
|
if activity_id:
|
||||||
activity = self._get_activity_by_id(activity_id)
|
home_activity = self._get_activity_by_id(activity_id)
|
||||||
|
|
||||||
if not activity:
|
if not home_activity:
|
||||||
activity = HomeActivity(bundle, activity_id)
|
home_activity = HomeActivity(activity_info, activity_id)
|
||||||
self._add_activity(activity)
|
self._add_activity(home_activity)
|
||||||
|
|
||||||
activity.set_window(window)
|
home_activity.set_window(window)
|
||||||
|
|
||||||
activity.props.launching = False
|
home_activity.props.launching = False
|
||||||
self.emit('activity-started', activity)
|
self.emit('activity-started', home_activity)
|
||||||
|
|
||||||
|
if self._pending_activity is None:
|
||||||
|
self._set_pending_activity(home_activity)
|
||||||
|
|
||||||
def _window_closed_cb(self, screen, window):
|
def _window_closed_cb(self, screen, window):
|
||||||
if window.get_window_type() == wnck.WINDOW_NORMAL:
|
if window.get_window_type() == wnck.WINDOW_NORMAL:
|
||||||
self._remove_activity_by_xid(window.get_xid())
|
self._remove_activity_by_xid(window.get_xid())
|
||||||
if not self._activities:
|
|
||||||
self.emit('active-activity-changed', None)
|
|
||||||
self._notify_activity_activation(self._current_activity, None)
|
|
||||||
|
|
||||||
def _get_activity_by_xid(self, xid):
|
def _get_activity_by_xid(self, xid):
|
||||||
for activity in self._activities:
|
for home_activity in self._activities:
|
||||||
if activity.get_xid() == xid:
|
if home_activity.get_xid() == xid:
|
||||||
return activity
|
return home_activity
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_activity_by_id(self, activity_id):
|
def _get_activity_by_id(self, activity_id):
|
||||||
for activity in self._activities:
|
for home_activity in self._activities:
|
||||||
if activity.get_activity_id() == activity_id:
|
if home_activity.get_activity_id() == activity_id:
|
||||||
return activity
|
return home_activity
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _notify_activity_activation(self, old_activity, new_activity):
|
|
||||||
if old_activity == new_activity:
|
|
||||||
return
|
|
||||||
|
|
||||||
if old_activity:
|
|
||||||
service = old_activity.get_service()
|
|
||||||
if service:
|
|
||||||
service.set_active(False,
|
|
||||||
reply_handler=self._set_active_success,
|
|
||||||
error_handler=self._set_active_error)
|
|
||||||
|
|
||||||
if new_activity:
|
|
||||||
service = new_activity.get_service()
|
|
||||||
if service:
|
|
||||||
service.set_active(True,
|
|
||||||
reply_handler=self._set_active_success,
|
|
||||||
error_handler=self._set_active_error)
|
|
||||||
|
|
||||||
def _set_active_success(self):
|
def _set_active_success(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -151,55 +184,58 @@ class HomeModel(gobject.GObject):
|
|||||||
|
|
||||||
def _active_window_changed_cb(self, screen):
|
def _active_window_changed_cb(self, screen):
|
||||||
window = screen.get_active_window()
|
window = screen.get_active_window()
|
||||||
if window == None:
|
if window is None or window.get_window_type() != wnck.WINDOW_NORMAL:
|
||||||
self.emit('active-activity-changed', None)
|
|
||||||
self._notify_activity_activation(self._current_activity, None)
|
|
||||||
return
|
|
||||||
if window.get_window_type() != wnck.WINDOW_NORMAL:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
xid = window.get_xid()
|
xid = window.get_xid()
|
||||||
act = self._get_activity_by_xid(window.get_xid())
|
act = self._get_activity_by_xid(xid)
|
||||||
if act:
|
if act is None:
|
||||||
self._notify_activity_activation(self._current_activity, act)
|
|
||||||
self._current_activity = act
|
|
||||||
else:
|
|
||||||
self._notify_activity_activation(self._current_activity, None)
|
|
||||||
self._current_activity = None
|
|
||||||
logging.error('Model for window %d does not exist.' % xid)
|
logging.error('Model for window %d does not exist.' % xid)
|
||||||
|
self._set_pending_activity(act)
|
||||||
|
self._set_active_activity(act)
|
||||||
|
|
||||||
self.emit('active-activity-changed', self._current_activity)
|
def _add_activity(self, home_activity):
|
||||||
|
self._activities.append(home_activity)
|
||||||
|
self.emit('activity-added', home_activity)
|
||||||
|
|
||||||
def _add_activity(self, activity):
|
def _remove_activity(self, home_activity):
|
||||||
self._activities.append(activity)
|
if home_activity == self._active_activity:
|
||||||
self.emit('activity-added', activity)
|
self._set_active_activity(None)
|
||||||
|
# Figure out the new _pending_activity.
|
||||||
|
windows = wnck.screen_get_default().get_windows_stacked()
|
||||||
|
windows.reverse()
|
||||||
|
for window in windows:
|
||||||
|
new_activity = self._get_activity_by_xid(window.get_xid())
|
||||||
|
if new_activity is not None:
|
||||||
|
self._set_pending_activity(new_activity)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logging.error('No activities are running')
|
||||||
|
self._set_pending_activity(None)
|
||||||
|
|
||||||
def _remove_activity(self, activity):
|
self.emit('activity-removed', home_activity)
|
||||||
if activity == self._current_activity:
|
self._activities.remove(home_activity)
|
||||||
self._current_activity = None
|
|
||||||
|
|
||||||
self.emit('activity-removed', activity)
|
|
||||||
self._activities.remove(activity)
|
|
||||||
|
|
||||||
def _remove_activity_by_xid(self, xid):
|
def _remove_activity_by_xid(self, xid):
|
||||||
activity = self._get_activity_by_xid(xid)
|
home_activity = self._get_activity_by_xid(xid)
|
||||||
if activity:
|
if home_activity:
|
||||||
self._remove_activity(activity)
|
self._remove_activity(home_activity)
|
||||||
else:
|
else:
|
||||||
logging.error('Model for window %d does not exist.' % xid)
|
logging.error('Model for window %d does not exist.' % xid)
|
||||||
|
|
||||||
def notify_activity_launch(self, activity_id, service_name):
|
def notify_activity_launch(self, activity_id, service_name):
|
||||||
bundle = self._bundle_registry.get_bundle(service_name)
|
registry = activity.get_registry()
|
||||||
if not bundle:
|
activity_info = registry.get_activity(service_name)
|
||||||
|
if not activity_info:
|
||||||
raise ValueError("Activity service name '%s' was not found in the bundle registry." % service_name)
|
raise ValueError("Activity service name '%s' was not found in the bundle registry." % service_name)
|
||||||
activity = HomeActivity(bundle, activity_id)
|
home_activity = HomeActivity(activity_info, activity_id)
|
||||||
activity.props.launching = True
|
home_activity.props.launching = True
|
||||||
self._add_activity(activity)
|
self._add_activity(home_activity)
|
||||||
|
|
||||||
def notify_activity_launch_failed(self, activity_id):
|
def notify_activity_launch_failed(self, activity_id):
|
||||||
activity = self._get_activity_by_id(activity_id)
|
home_activity = self._get_activity_by_id(activity_id)
|
||||||
if activity:
|
if home_activity:
|
||||||
logging.debug("Activity %s (%s) launch failed" % (activity_id, activity.get_type()))
|
logging.debug("Activity %s (%s) launch failed" % (activity_id, home_activity.get_type()))
|
||||||
self._remove_activity(activity)
|
self._remove_activity(home_activity)
|
||||||
else:
|
else:
|
||||||
logging.error('Model for activity id %s does not exist.' % activity_id)
|
logging.error('Model for activity id %s does not exist.' % activity_id)
|
||||||
|
@ -17,10 +17,7 @@
|
|||||||
"""D-bus service providing access to the shell's functionality"""
|
"""D-bus service providing access to the shell's functionality"""
|
||||||
import dbus
|
import dbus
|
||||||
|
|
||||||
from model import bundleregistry
|
|
||||||
|
|
||||||
_DBUS_SERVICE = "org.laptop.Shell"
|
_DBUS_SERVICE = "org.laptop.Shell"
|
||||||
_DBUS_ACTIVITY_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry"
|
|
||||||
_DBUS_SHELL_IFACE = "org.laptop.Shell"
|
_DBUS_SHELL_IFACE = "org.laptop.Shell"
|
||||||
_DBUS_OWNER_IFACE = "org.laptop.Shell.Owner"
|
_DBUS_OWNER_IFACE = "org.laptop.Shell.Owner"
|
||||||
_DBUS_PATH = "/org/laptop/Shell"
|
_DBUS_PATH = "/org/laptop/Shell"
|
||||||
@ -56,9 +53,6 @@ class ShellService(dbus.service.Object):
|
|||||||
self._home_model.connect('active-activity-changed',
|
self._home_model.connect('active-activity-changed',
|
||||||
self._cur_activity_changed_cb)
|
self._cur_activity_changed_cb)
|
||||||
|
|
||||||
bundle_registry = bundleregistry.get_registry()
|
|
||||||
bundle_registry.connect('bundle-added', self._bundle_added_cb)
|
|
||||||
|
|
||||||
bus = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
bus_name = dbus.service.BusName(_DBUS_SERVICE, bus=bus)
|
bus_name = dbus.service.BusName(_DBUS_SERVICE, bus=bus)
|
||||||
dbus.service.Object.__init__(self, bus_name, _DBUS_PATH)
|
dbus.service.Object.__init__(self, bus_name, _DBUS_PATH)
|
||||||
@ -83,60 +77,6 @@ class ShellService(dbus.service.Object):
|
|||||||
def NotifyLaunchFailure(self, activity_id):
|
def NotifyLaunchFailure(self, activity_id):
|
||||||
self._shell.notify_launch_failure(activity_id)
|
self._shell.notify_launch_failure(activity_id)
|
||||||
|
|
||||||
@dbus.service.method(_DBUS_ACTIVITY_REGISTRY_IFACE,
|
|
||||||
in_signature="s", out_signature="b")
|
|
||||||
def AddBundle(self, bundle_path):
|
|
||||||
"""Register the activity bundle with the global registry
|
|
||||||
|
|
||||||
bundle_path -- path to the activity bundle's root directory,
|
|
||||||
that is, the directory with activity/activity.info as a
|
|
||||||
child of the directory.
|
|
||||||
|
|
||||||
The bundleregistry.BundleRegistry is responsible for setting
|
|
||||||
up a set of d-bus service mappings for each available activity.
|
|
||||||
"""
|
|
||||||
registry = bundleregistry.get_registry()
|
|
||||||
return registry.add_bundle(bundle_path)
|
|
||||||
|
|
||||||
@dbus.service.method(_DBUS_ACTIVITY_REGISTRY_IFACE,
|
|
||||||
in_signature="s", out_signature="a{sv}")
|
|
||||||
def GetActivity(self, service_name):
|
|
||||||
registry = bundleregistry.get_registry()
|
|
||||||
bundle = registry.get_bundle(service_name)
|
|
||||||
if not bundle:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
return self._bundle_to_dict(bundle)
|
|
||||||
|
|
||||||
@dbus.service.method(_DBUS_ACTIVITY_REGISTRY_IFACE,
|
|
||||||
in_signature="s", out_signature="aa{sv}")
|
|
||||||
def FindActivity(self, name):
|
|
||||||
result = []
|
|
||||||
key = name.lower()
|
|
||||||
|
|
||||||
for bundle in bundleregistry.get_registry():
|
|
||||||
name = bundle.get_name().lower()
|
|
||||||
service_name = bundle.get_service_name().lower()
|
|
||||||
if name.find(key) != -1 or service_name.find(key) != -1:
|
|
||||||
result.append(self._bundle_to_dict(bundle))
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
@dbus.service.method(_DBUS_ACTIVITY_REGISTRY_IFACE,
|
|
||||||
in_signature="s", out_signature="aa{sv}")
|
|
||||||
def GetActivitiesForType(self, mime_type):
|
|
||||||
result = []
|
|
||||||
|
|
||||||
for bundle in bundleregistry.get_registry():
|
|
||||||
if bundle.get_mime_types() and mime_type in bundle.get_mime_types():
|
|
||||||
result.append(self._bundle_to_dict(bundle))
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
@dbus.service.signal(_DBUS_ACTIVITY_REGISTRY_IFACE, signature="a{sv}")
|
|
||||||
def ActivityAdded(self, activity_info):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
|
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
|
||||||
def ColorChanged(self, color):
|
def ColorChanged(self, color):
|
||||||
pass
|
pass
|
||||||
@ -169,12 +109,3 @@ class ShellService(dbus.service.Object):
|
|||||||
if new_id:
|
if new_id:
|
||||||
self.CurrentActivityChanged(new_id)
|
self.CurrentActivityChanged(new_id)
|
||||||
|
|
||||||
def _bundle_to_dict(self, bundle):
|
|
||||||
return {'name': bundle.get_name(),
|
|
||||||
'icon': bundle.get_icon(),
|
|
||||||
'service_name': bundle.get_service_name(),
|
|
||||||
'path': bundle.get_path()}
|
|
||||||
|
|
||||||
def _bundle_added_cb(self, bundle_registry, bundle):
|
|
||||||
self.ActivityAdded(self._bundle_to_dict(bundle))
|
|
||||||
|
|
||||||
|
@ -85,18 +85,19 @@ class BuddyMenu(Palette):
|
|||||||
else:
|
else:
|
||||||
menu_item = MenuItem(_('Make friend'), 'stock-add')
|
menu_item = MenuItem(_('Make friend'), 'stock-add')
|
||||||
menu_item.connect('activate', self._make_friend_cb)
|
menu_item.connect('activate', self._make_friend_cb)
|
||||||
self.append_menu_item(menu_item)
|
|
||||||
|
self.menu.append(menu_item)
|
||||||
menu_item.show()
|
menu_item.show()
|
||||||
|
|
||||||
activity = shell_model.get_home().get_current_activity()
|
activity = self._shell.get_current_activity()
|
||||||
if activity != None:
|
if activity != None:
|
||||||
activity_ps = pservice.get_activity(activity.get_activity_id())
|
activity_ps = pservice.get_activity(activity.get_id())
|
||||||
|
|
||||||
# FIXME check that the buddy is not in the activity already
|
# FIXME check that the buddy is not in the activity already
|
||||||
|
|
||||||
menu_item = MenuItem(_('Invite'), 'stock-invite')
|
menu_item = MenuItem(_('Invite'), 'stock-invite')
|
||||||
menu_item.connect('activate', self._invite_friend_cb)
|
menu_item.connect('activate', self._invite_friend_cb)
|
||||||
self.append_menu_item(menu_item)
|
self.menu.append(menu_item)
|
||||||
menu_item.show()
|
menu_item.show()
|
||||||
|
|
||||||
def _buddy_icon_changed_cb(self, buddy):
|
def _buddy_icon_changed_cb(self, buddy):
|
||||||
|
@ -26,6 +26,7 @@ import gtk
|
|||||||
import wnck
|
import wnck
|
||||||
|
|
||||||
from sugar.activity.activityhandle import ActivityHandle
|
from sugar.activity.activityhandle import ActivityHandle
|
||||||
|
from sugar import activity
|
||||||
from sugar.activity import activityfactory
|
from sugar.activity import activityfactory
|
||||||
from sugar.datastore import datastore
|
from sugar.datastore import datastore
|
||||||
from sugar import profile
|
from sugar import profile
|
||||||
@ -34,7 +35,6 @@ from view.ActivityHost import ActivityHost
|
|||||||
from view.frame.frame import Frame
|
from view.frame.frame import Frame
|
||||||
from view.keyhandler import KeyHandler
|
from view.keyhandler import KeyHandler
|
||||||
from view.home.HomeWindow import HomeWindow
|
from view.home.HomeWindow import HomeWindow
|
||||||
from model import bundleregistry
|
|
||||||
from model.shellmodel import ShellModel
|
from model.shellmodel import ShellModel
|
||||||
from hardware import hardwaremanager
|
from hardware import hardwaremanager
|
||||||
|
|
||||||
@ -47,6 +47,7 @@ class Shell(gobject.GObject):
|
|||||||
self._hosts = {}
|
self._hosts = {}
|
||||||
self._screen = wnck.screen_get_default()
|
self._screen = wnck.screen_get_default()
|
||||||
self._current_host = None
|
self._current_host = None
|
||||||
|
self._pending_host = None
|
||||||
self._screen_rotation = 0
|
self._screen_rotation = 0
|
||||||
|
|
||||||
self._key_handler = KeyHandler(self)
|
self._key_handler = KeyHandler(self)
|
||||||
@ -65,6 +66,8 @@ class Shell(gobject.GObject):
|
|||||||
home_model.connect('activity-removed', self._activity_removed_cb)
|
home_model.connect('activity-removed', self._activity_removed_cb)
|
||||||
home_model.connect('active-activity-changed',
|
home_model.connect('active-activity-changed',
|
||||||
self._active_activity_changed_cb)
|
self._active_activity_changed_cb)
|
||||||
|
home_model.connect('pending-activity-changed',
|
||||||
|
self._pending_activity_changed_cb)
|
||||||
|
|
||||||
# Unfreeze the display when it's stable
|
# Unfreeze the display when it's stable
|
||||||
hw_manager = hardwaremanager.get_manager()
|
hw_manager = hardwaremanager.get_manager()
|
||||||
@ -100,6 +103,12 @@ class Shell(gobject.GObject):
|
|||||||
|
|
||||||
self._current_host = host
|
self._current_host = host
|
||||||
|
|
||||||
|
def _pending_activity_changed_cb(self, home_model, home_activity):
|
||||||
|
if home_activity:
|
||||||
|
self._pending_host = self._hosts[home_activity.get_xid()]
|
||||||
|
else:
|
||||||
|
self._pending_host = None
|
||||||
|
|
||||||
def get_model(self):
|
def get_model(self):
|
||||||
return self._model
|
return self._model
|
||||||
|
|
||||||
@ -107,16 +116,16 @@ class Shell(gobject.GObject):
|
|||||||
return self._frame
|
return self._frame
|
||||||
|
|
||||||
def join_activity(self, bundle_id, activity_id):
|
def join_activity(self, bundle_id, activity_id):
|
||||||
activity = self.get_activity(activity_id)
|
activity_host = self.get_activity(activity_id)
|
||||||
if activity:
|
if activity_host:
|
||||||
activity.present()
|
activity_host.present()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get the service name for this activity, if
|
# Get the service name for this activity, if
|
||||||
# we have a bundle on the system capable of handling
|
# we have a bundle on the system capable of handling
|
||||||
# this activity type
|
# this activity type
|
||||||
breg = bundleregistry.get_registry()
|
registry = activity.get_registry()
|
||||||
bundle = breg.get_bundle(bundle_id)
|
bundle = registry.get_activity(bundle_id)
|
||||||
if not bundle:
|
if not bundle:
|
||||||
logging.error("Couldn't find activity for type %s" % bundle_id)
|
logging.error("Couldn't find activity for type %s" % bundle_id)
|
||||||
return
|
return
|
||||||
@ -156,6 +165,8 @@ class Shell(gobject.GObject):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if level == ShellModel.ZOOM_ACTIVITY:
|
if level == ShellModel.ZOOM_ACTIVITY:
|
||||||
|
if self._pending_host is not None:
|
||||||
|
self._pending_host.present()
|
||||||
self._screen.toggle_showing_desktop(False)
|
self._screen.toggle_showing_desktop(False)
|
||||||
else:
|
else:
|
||||||
self._model.set_zoom_level(level)
|
self._model.set_zoom_level(level)
|
||||||
|
@ -73,9 +73,9 @@ class ClipboardIcon(CanvasIcon):
|
|||||||
self._selected = selected
|
self._selected = selected
|
||||||
if selected:
|
if selected:
|
||||||
if not self._hover:
|
if not self._hover:
|
||||||
self.props.background_color = style.COLOR_PANEL_GREY.get_int()
|
self.props.background_color = style.COLOR_SELECTION_GREY.get_int()
|
||||||
else:
|
else:
|
||||||
self.props.background_color = style.COLOR_TOOLBAR_GREY.get_int()
|
self.props.background_color = style.COLOR_PANEL_GREY.get_int()
|
||||||
|
|
||||||
def set_state(self, name, percent, icon_name, preview, activity):
|
def set_state(self, name, percent, icon_name, preview, activity):
|
||||||
cb_service = clipboardservice.get_instance()
|
cb_service = clipboardservice.get_instance()
|
||||||
@ -107,11 +107,11 @@ class ClipboardIcon(CanvasIcon):
|
|||||||
def prelight(self, enter):
|
def prelight(self, enter):
|
||||||
if enter:
|
if enter:
|
||||||
self._hover = True
|
self._hover = True
|
||||||
self.props.background_color = color.BLACK.get_int()
|
self.props.background_color = style.COLOR_BLACK.get_int()
|
||||||
else:
|
else:
|
||||||
self._hover = False
|
self._hover = False
|
||||||
if self._selected:
|
if self._selected:
|
||||||
self.props.background_color = color.DESKTOP_BACKGROUND.get_int()
|
self.props.background_color = style.COLOR_SELECTION_GREY.get_int()
|
||||||
else:
|
else:
|
||||||
self.props.background_color = color.TOOLBAR_BACKGROUND.get_int()
|
self.props.background_color = style.COLOR_PANEL_GREY.get_int()
|
||||||
|
|
||||||
|
@ -64,11 +64,13 @@ class ClipboardMenu(Palette):
|
|||||||
|
|
||||||
self._remove_item = MenuItem(_('Remove'), 'stock-remove')
|
self._remove_item = MenuItem(_('Remove'), 'stock-remove')
|
||||||
self._remove_item.connect('activate', self._remove_item_activate_cb)
|
self._remove_item.connect('activate', self._remove_item_activate_cb)
|
||||||
self.append_menu_item(self._remove_item)
|
self.menu.append(self._remove_item)
|
||||||
|
self._remove_item.show()
|
||||||
|
|
||||||
self._open_item = MenuItem(_('Open'), 'stock-keep')
|
self._open_item = MenuItem(_('Open'), 'stock-keep')
|
||||||
self._open_item.connect('activate', self._open_item_activate_cb)
|
self._open_item.connect('activate', self._open_item_activate_cb)
|
||||||
self.append_menu_item(self._open_item)
|
self.menu.append(self._open_item)
|
||||||
|
self._open_item.show()
|
||||||
|
|
||||||
#self._stop_item = MenuItem(_('Stop download'), 'stock-close')
|
#self._stop_item = MenuItem(_('Stop download'), 'stock-close')
|
||||||
# TODO: Implement stopping downloads
|
# TODO: Implement stopping downloads
|
||||||
@ -77,7 +79,8 @@ class ClipboardMenu(Palette):
|
|||||||
|
|
||||||
self._journal_item = MenuItem(_('Add to journal'), 'document-save')
|
self._journal_item = MenuItem(_('Add to journal'), 'document-save')
|
||||||
self._journal_item.connect('activate', self._journal_item_activate_cb)
|
self._journal_item.connect('activate', self._journal_item_activate_cb)
|
||||||
self.append_menu_item(self._journal_item)
|
self.menu.append(self._journal_item)
|
||||||
|
self._journal_item.show()
|
||||||
|
|
||||||
self._update_items_visibility(installable)
|
self._update_items_visibility(installable)
|
||||||
|
|
||||||
@ -120,32 +123,8 @@ class ClipboardMenu(Palette):
|
|||||||
def _open_item_activate_cb(self, menu_item):
|
def _open_item_activate_cb(self, menu_item):
|
||||||
if self._percent < 100:
|
if self._percent < 100:
|
||||||
return
|
return
|
||||||
|
|
||||||
jobject = self._copy_to_journal()
|
jobject = self._copy_to_journal()
|
||||||
# TODO: we cannot simply call resume() right now because we would lock
|
jobject.resume()
|
||||||
# the shell as we are sharing the same loop as the shell service.
|
|
||||||
#jobject.resume()
|
|
||||||
|
|
||||||
# TODO: take this out when we fix the mess that is the shell/shellservice.
|
|
||||||
from model import bundleregistry
|
|
||||||
from sugar.activity.bundle import Bundle
|
|
||||||
from sugar.activity import activityfactory
|
|
||||||
if jobject.is_bundle():
|
|
||||||
bundle = Bundle(jobject.file_path)
|
|
||||||
if not bundle.is_installed():
|
|
||||||
bundle.install()
|
|
||||||
|
|
||||||
activityfactory.create(bundle.get_service_name())
|
|
||||||
else:
|
|
||||||
service_name = None
|
|
||||||
mime_type = jobject.metadata['mime_type']
|
|
||||||
for bundle in bundleregistry.get_registry():
|
|
||||||
if bundle.get_mime_types() and mime_type in bundle.get_mime_types():
|
|
||||||
service_name = bundle.get_service_name()
|
|
||||||
break
|
|
||||||
if service_name:
|
|
||||||
activityfactory.create_with_object_id(service_name,
|
|
||||||
jobject.object_id)
|
|
||||||
|
|
||||||
def _remove_item_activate_cb(self, menu_item):
|
def _remove_item_activate_cb(self, menu_item):
|
||||||
cb_service = clipboardservice.get_instance()
|
cb_service = clipboardservice.get_instance()
|
||||||
|
@ -20,30 +20,33 @@ import logging
|
|||||||
from sugar.graphics.palette import Palette
|
from sugar.graphics.palette import Palette
|
||||||
from sugar.graphics.xocolor import XoColor
|
from sugar.graphics.xocolor import XoColor
|
||||||
from sugar.graphics.iconbutton import IconButton
|
from sugar.graphics.iconbutton import IconButton
|
||||||
|
from sugar.graphics import style
|
||||||
from sugar import profile
|
from sugar import profile
|
||||||
|
from sugar import activity
|
||||||
|
|
||||||
from model import bundleregistry
|
|
||||||
from frameinvoker import FrameCanvasInvoker
|
from frameinvoker import FrameCanvasInvoker
|
||||||
|
|
||||||
class ActivityButton(IconButton):
|
class ActivityButton(IconButton):
|
||||||
def __init__(self, activity):
|
def __init__(self, activity_info):
|
||||||
IconButton.__init__(self, icon_name=activity.get_icon())
|
IconButton.__init__(self, icon_name=activity_info.icon,
|
||||||
|
stroke_color=style.COLOR_WHITE,
|
||||||
|
fill_color=style.COLOR_TRANSPARENT)
|
||||||
|
|
||||||
palette = Palette(activity.get_name())
|
palette = Palette(activity_info.name)
|
||||||
palette.props.invoker = FrameCanvasInvoker(self)
|
palette.props.invoker = FrameCanvasInvoker(self)
|
||||||
palette.set_group_id('frame')
|
palette.set_group_id('frame')
|
||||||
self.set_palette(palette)
|
self.set_palette(palette)
|
||||||
|
|
||||||
self._activity = activity
|
self._activity_info = activity_info
|
||||||
|
|
||||||
def get_bundle_id(self):
|
def get_bundle_id(self):
|
||||||
return self._activity.get_service_name()
|
return self._activity_info.service_name
|
||||||
|
|
||||||
class InviteButton(IconButton):
|
class InviteButton(IconButton):
|
||||||
def __init__(self, activity, invite):
|
def __init__(self, activity_model, invite):
|
||||||
IconButton.__init__(self, icon_name=activity.get_icon())
|
IconButton.__init__(self, icon_name=activity_model.get_color())
|
||||||
|
|
||||||
self.props.xo_color = activity.get_color()
|
self.props.xo_color = activity_model.get_color()
|
||||||
self._invite = invite
|
self._invite = invite
|
||||||
|
|
||||||
def get_activity_id(self):
|
def get_activity_id(self):
|
||||||
@ -64,12 +67,12 @@ class ActivitiesBox(hippo.CanvasBox):
|
|||||||
self._invite_to_item = {}
|
self._invite_to_item = {}
|
||||||
self._invites = self._shell_model.get_invites()
|
self._invites = self._shell_model.get_invites()
|
||||||
|
|
||||||
bundle_registry = bundleregistry.get_registry()
|
registry = activity.get_registry()
|
||||||
for bundle in bundle_registry:
|
for activity_info in registry.get_activities():
|
||||||
if bundle.get_show_launcher():
|
if activity_info.show_launcher:
|
||||||
self.add_activity(bundle)
|
self.add_activity(activity_info)
|
||||||
|
|
||||||
bundle_registry.connect('bundle-added', self._bundle_added_cb)
|
registry.connect('activity-added', self._activity_added_cb)
|
||||||
|
|
||||||
for invite in self._invites:
|
for invite in self._invites:
|
||||||
self.add_invite(invite)
|
self.add_invite(invite)
|
||||||
@ -90,19 +93,19 @@ class ActivitiesBox(hippo.CanvasBox):
|
|||||||
def _invite_removed_cb(self, invites, invite):
|
def _invite_removed_cb(self, invites, invite):
|
||||||
self.remove_invite(invite)
|
self.remove_invite(invite)
|
||||||
|
|
||||||
def _bundle_added_cb(self, bundle_registry, bundle):
|
def _activity_added_cb(self, activity_registry, activity_info):
|
||||||
self.add_activity(bundle)
|
self.add_activity(activity_info)
|
||||||
|
|
||||||
def add_activity(self, activity):
|
def add_activity(self, activity_info):
|
||||||
item = ActivityButton(activity)
|
item = ActivityButton(activity_info)
|
||||||
item.connect('activated', self._activity_clicked_cb)
|
item.connect('activated', self._activity_clicked_cb)
|
||||||
self.append(item, 0)
|
self.append(item, 0)
|
||||||
|
|
||||||
def add_invite(self, invite):
|
def add_invite(self, invite):
|
||||||
mesh = self._shell_model.get_mesh()
|
mesh = self._shell_model.get_mesh()
|
||||||
activity = mesh.get_activity(invite.get_activity_id())
|
activity_model = mesh.get_activity(invite.get_activity_id())
|
||||||
if activity:
|
if activity:
|
||||||
item = InviteButton(activity, invite)
|
item = InviteButton(activity_model, invite)
|
||||||
item.connect('activated', self._invite_clicked_cb)
|
item.connect('activated', self._invite_clicked_cb)
|
||||||
self.append(item, 0)
|
self.append(item, 0)
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ import gobject
|
|||||||
from sugar.graphics.canvasicon import CanvasIcon
|
from sugar.graphics.canvasicon import CanvasIcon
|
||||||
from sugar.graphics import style
|
from sugar.graphics import style
|
||||||
from sugar.presence import presenceservice
|
from sugar.presence import presenceservice
|
||||||
|
from sugar import activity
|
||||||
|
|
||||||
from model import bundleregistry
|
|
||||||
from view.BuddyIcon import BuddyIcon
|
from view.BuddyIcon import BuddyIcon
|
||||||
|
|
||||||
class FriendView(hippo.CanvasBox):
|
class FriendView(hippo.CanvasBox):
|
||||||
@ -46,9 +46,9 @@ class FriendView(hippo.CanvasBox):
|
|||||||
self._buddy.connect('disappeared', self._buddy_disappeared_cb)
|
self._buddy.connect('disappeared', self._buddy_disappeared_cb)
|
||||||
self._buddy.connect('color-changed', self._buddy_color_changed_cb)
|
self._buddy.connect('color-changed', self._buddy_color_changed_cb)
|
||||||
|
|
||||||
def _get_new_icon_name(self, activity):
|
def _get_new_icon_name(self, home_activity):
|
||||||
registry = bundleregistry.get_registry()
|
registry = activity.get_registry()
|
||||||
bundle = registry.get_bundle(activity.get_type())
|
bundle = registry.get_bundle(home_activity.get_type())
|
||||||
if bundle:
|
if bundle:
|
||||||
return bundle.get_icon()
|
return bundle.get_icon()
|
||||||
return None
|
return None
|
||||||
@ -58,14 +58,14 @@ class FriendView(hippo.CanvasBox):
|
|||||||
self.remove(self._activity_icon)
|
self.remove(self._activity_icon)
|
||||||
self._activity_icon_visible = False
|
self._activity_icon_visible = False
|
||||||
|
|
||||||
def _buddy_activity_changed_cb(self, buddy, activity=None):
|
def _buddy_activity_changed_cb(self, buddy, home_activity=None):
|
||||||
if not activity:
|
if not home_activity:
|
||||||
self._remove_activity_icon()
|
self._remove_activity_icon()
|
||||||
return
|
return
|
||||||
|
|
||||||
# FIXME: use some sort of "unknown activity" icon rather
|
# FIXME: use some sort of "unknown activity" icon rather
|
||||||
# than hiding the icon?
|
# than hiding the icon?
|
||||||
name = self._get_new_icon_name(activity)
|
name = self._get_new_icon_name(home_activity)
|
||||||
if name:
|
if name:
|
||||||
self._activity_icon.props.icon_name = name
|
self._activity_icon.props.icon_name = name
|
||||||
self._activity_icon.props.xo_color = buddy.get_color()
|
self._activity_icon.props.xo_color = buddy.get_color()
|
||||||
@ -76,8 +76,8 @@ class FriendView(hippo.CanvasBox):
|
|||||||
self._remove_activity_icon()
|
self._remove_activity_icon()
|
||||||
|
|
||||||
def _buddy_appeared_cb(self, buddy):
|
def _buddy_appeared_cb(self, buddy):
|
||||||
activity = self._buddy.get_current_activity()
|
home_activity = self._buddy.get_current_activity()
|
||||||
self._buddy_activity_changed_cb(buddy, activity)
|
self._buddy_activity_changed_cb(buddy, home_activity)
|
||||||
|
|
||||||
def _buddy_disappeared_cb(self, buddy):
|
def _buddy_disappeared_cb(self, buddy):
|
||||||
self._buddy_activity_changed_cb(buddy, None)
|
self._buddy_activity_changed_cb(buddy, None)
|
||||||
|
@ -130,7 +130,7 @@ class HomeMyIcon(MyIcon):
|
|||||||
|
|
||||||
shutdown_menu_item = gtk.MenuItem(_('Shutdown'))
|
shutdown_menu_item = gtk.MenuItem(_('Shutdown'))
|
||||||
shutdown_menu_item.connect('activate', self._shutdown_activate_cb)
|
shutdown_menu_item.connect('activate', self._shutdown_activate_cb)
|
||||||
palette.append_menu_item(shutdown_menu_item)
|
palette.menu.append(shutdown_menu_item)
|
||||||
shutdown_menu_item.show()
|
shutdown_menu_item.show()
|
||||||
|
|
||||||
self.set_palette(palette)
|
self.set_palette(palette)
|
||||||
|
@ -88,14 +88,14 @@ class ActivityIcon(CanvasIcon):
|
|||||||
|
|
||||||
resume_menu_item = gtk.MenuItem(_('Resume'))
|
resume_menu_item = gtk.MenuItem(_('Resume'))
|
||||||
resume_menu_item.connect('activate', self._resume_activate_cb)
|
resume_menu_item.connect('activate', self._resume_activate_cb)
|
||||||
palette.append_menu_item(resume_menu_item)
|
palette.menu.append(resume_menu_item)
|
||||||
resume_menu_item.show()
|
resume_menu_item.show()
|
||||||
|
|
||||||
# FIXME: kludge
|
# FIXME: kludge
|
||||||
if self._activity.get_type() != "org.laptop.JournalActivity":
|
if self._activity.get_type() != "org.laptop.JournalActivity":
|
||||||
stop_menu_item = gtk.MenuItem(_('Stop'))
|
stop_menu_item = gtk.MenuItem(_('Stop'))
|
||||||
stop_menu_item.connect('activate', self._stop_activate_cb)
|
stop_menu_item.connect('activate', self._stop_activate_cb)
|
||||||
palette.append_menu_item(stop_menu_item)
|
palette.menu.append(stop_menu_item)
|
||||||
stop_menu_item.show()
|
stop_menu_item.show()
|
||||||
|
|
||||||
def _launching_changed_cb(self, activity, pspec):
|
def _launching_changed_cb(self, activity, pspec):
|
||||||
@ -189,7 +189,7 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
self._model = shell.get_model().get_home()
|
self._model = shell.get_model().get_home()
|
||||||
self._model.connect('activity-added', self._activity_added_cb)
|
self._model.connect('activity-added', self._activity_added_cb)
|
||||||
self._model.connect('activity-removed', self._activity_removed_cb)
|
self._model.connect('activity-removed', self._activity_removed_cb)
|
||||||
self._model.connect('active-activity-changed', self._activity_changed_cb)
|
self._model.connect('pending-activity-changed', self._activity_changed_cb)
|
||||||
|
|
||||||
self.connect('button-release-event', self._button_release_event_cb)
|
self.connect('button-release-event', self._button_release_event_cb)
|
||||||
|
|
||||||
@ -385,7 +385,7 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
cr.fill()
|
cr.fill()
|
||||||
|
|
||||||
# Selected Wedge
|
# Selected Wedge
|
||||||
current_activity = self._model.get_current_activity()
|
current_activity = self._model.get_pending_activity()
|
||||||
if current_activity is not None:
|
if current_activity is not None:
|
||||||
selected_index = self._model.index(current_activity)
|
selected_index = self._model.index(current_activity)
|
||||||
[angle_start, angle_end] = self._get_angles(selected_index)
|
[angle_start, angle_end] = self._get_angles(selected_index)
|
||||||
|
@ -12,21 +12,19 @@ sugar_PYTHON = \
|
|||||||
util.py \
|
util.py \
|
||||||
wm.py
|
wm.py
|
||||||
|
|
||||||
INCLUDES = \
|
|
||||||
$(LIB_CFLAGS) \
|
|
||||||
$(LIB_BINDINGS_CFLAGS) \
|
|
||||||
$(PYTHON_INCLUDES) \
|
|
||||||
-I$(top_srcdir)/lib \
|
|
||||||
-I$(top_srcdir)/lib/ui
|
|
||||||
|
|
||||||
pkgpyexecdir = $(pythondir)/sugar
|
pkgpyexecdir = $(pythondir)/sugar
|
||||||
|
|
||||||
pkgpyexec_LTLIBRARIES = _sugarext.la _sugaruiext.la
|
pkgpyexec_LTLIBRARIES = _sugarext.la _sugaruiext.la
|
||||||
|
|
||||||
|
_sugarext_la_CFLAGS = \
|
||||||
|
$(LIB_CFLAGS) \
|
||||||
|
$(LIB_BINDINGS_CFLAGS) \
|
||||||
|
$(PYTHON_INCLUDES) \
|
||||||
|
-I$(top_srcdir)/lib
|
||||||
|
|
||||||
_sugarext_la_LDFLAGS = -module -avoid-version
|
_sugarext_la_LDFLAGS = -module -avoid-version
|
||||||
_sugarext_la_LIBADD = \
|
_sugarext_la_LIBADD = \
|
||||||
$(LIB_BINDINGS_LIBS) \
|
$(LIB_BINDINGS_LIBS) \
|
||||||
$(LIB_LIBS) \
|
|
||||||
$(top_builddir)/lib/libsugar.la
|
$(top_builddir)/lib/libsugar.la
|
||||||
|
|
||||||
_sugarext_la_SOURCES = \
|
_sugarext_la_SOURCES = \
|
||||||
@ -36,10 +34,16 @@ nodist__sugarext_la_SOURCES = _sugarext.c
|
|||||||
|
|
||||||
_sugarext.c: _sugarext.defs _sugarext.override
|
_sugarext.c: _sugarext.defs _sugarext.override
|
||||||
|
|
||||||
|
_sugaruiext_la_CFLAGS = \
|
||||||
|
$(LIBUI_CFLAGS) \
|
||||||
|
$(LIBUI_BINDINGS_CFLAGS) \
|
||||||
|
$(PYTHON_INCLUDES) \
|
||||||
|
-I$(top_srcdir)/lib/ui
|
||||||
|
|
||||||
_sugaruiext_la_LDFLAGS = -module -avoid-version
|
_sugaruiext_la_LDFLAGS = -module -avoid-version
|
||||||
_sugaruiext_la_LIBADD = \
|
_sugaruiext_la_LIBADD = \
|
||||||
$(LIB_BINDINGS_LIBS) \
|
$(LIBUI_BINDINGS_LIBS) \
|
||||||
$(LIB_LIBS) \
|
$(LIBUI_LIBS) \
|
||||||
$(top_builddir)/lib/ui/libsugarui.la
|
$(top_builddir)/lib/ui/libsugarui.la
|
||||||
|
|
||||||
_sugaruiext_la_SOURCES = \
|
_sugaruiext_la_SOURCES = \
|
||||||
|
@ -26,19 +26,27 @@
|
|||||||
|
|
||||||
;; From sugar-menu.h
|
;; From sugar-menu.h
|
||||||
|
|
||||||
(define-method popup
|
(define-method set_active
|
||||||
(of-object "SugarMenu")
|
(of-object "SugarMenu")
|
||||||
(c-name "sugar_menu_popup")
|
(c-name "sugar_menu_set_active")
|
||||||
(return-type "none")
|
(return-type "none")
|
||||||
(parameters
|
(parameters
|
||||||
'("gint" "x")
|
'("gboolean" "active")
|
||||||
'("gint" "y")
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
(define-method popdown
|
(define-method embed
|
||||||
(of-object "SugarMenu")
|
(of-object "SugarMenu")
|
||||||
(c-name "sugar_menu_popdown")
|
(c-name "sugar_menu_embed")
|
||||||
|
(return-type "none")
|
||||||
|
(parameters
|
||||||
|
'("GtkContainer" "container")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(define-method unembed
|
||||||
|
(of-object "SugarMenu")
|
||||||
|
(c-name "sugar_menu_unembed")
|
||||||
(return-type "none")
|
(return-type "none")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ modulename _sugarext
|
|||||||
import gobject.GObject as PyGObject_Type
|
import gobject.GObject as PyGObject_Type
|
||||||
import gtk.Entry as PyGtkEntry_Type
|
import gtk.Entry as PyGtkEntry_Type
|
||||||
import gtk.Menu as PyGtkMenu_Type
|
import gtk.Menu as PyGtkMenu_Type
|
||||||
|
import gtk.Container as PyGtkContainer_Type
|
||||||
import gtk.gdk.Window as PyGdkWindow_Type
|
import gtk.gdk.Window as PyGdkWindow_Type
|
||||||
%%
|
%%
|
||||||
ignore-glob
|
ignore-glob
|
||||||
|
@ -114,7 +114,7 @@ class ActivityCreationHandler(gobject.GObject):
|
|||||||
|
|
||||||
self._factory.create(self._activity_handle.get_dict(),
|
self._factory.create(self._activity_handle.get_dict(),
|
||||||
timeout=120 * 1000,
|
timeout=120 * 1000,
|
||||||
reply_handler=self._no_reply_handler,
|
reply_handler=self._create_reply_handler,
|
||||||
error_handler=self._create_error_handler)
|
error_handler=self._create_error_handler)
|
||||||
|
|
||||||
def get_activity_id(self):
|
def get_activity_id(self):
|
||||||
@ -137,7 +137,10 @@ class ActivityCreationHandler(gobject.GObject):
|
|||||||
def _activate_error_handler(self, err):
|
def _activate_error_handler(self, err):
|
||||||
logging.debug("Activity activation request failed %s" % err)
|
logging.debug("Activity activation request failed %s" % err)
|
||||||
|
|
||||||
def _create_reply_handler(self, xid):
|
def _create_reply_handler(self, xid=None):
|
||||||
|
if xid is None:
|
||||||
|
self._create_error_handler('D-Bus error')
|
||||||
|
return
|
||||||
logging.debug("Activity created %s (%s)." %
|
logging.debug("Activity created %s (%s)." %
|
||||||
(self._activity_handle.activity_id, self._service_name))
|
(self._activity_handle.activity_id, self._service_name))
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ class NotInstalledException(Exception): pass
|
|||||||
class InvalidPathException(Exception): pass
|
class InvalidPathException(Exception): pass
|
||||||
class ZipExtractException(Exception): pass
|
class ZipExtractException(Exception): pass
|
||||||
class RegistrationException(Exception): pass
|
class RegistrationException(Exception): pass
|
||||||
|
class MalformedBundleException(Exception): pass
|
||||||
|
|
||||||
class Bundle:
|
class Bundle:
|
||||||
"""Metadata description of a given application/activity
|
"""Metadata description of a given application/activity
|
||||||
@ -265,10 +266,12 @@ class Bundle:
|
|||||||
if not bundle_root_dir:
|
if not bundle_root_dir:
|
||||||
bundle_root_dir = file_name.split('/')[0]
|
bundle_root_dir = file_name.split('/')[0]
|
||||||
if not bundle_root_dir.endswith('.activity'):
|
if not bundle_root_dir.endswith('.activity'):
|
||||||
raise 'Incorrect bundle.'
|
raise MalformedBundleException(
|
||||||
|
'The activity directory name must end with .activity')
|
||||||
else:
|
else:
|
||||||
if not file_name.startswith(bundle_root_dir):
|
if not file_name.startswith(bundle_root_dir):
|
||||||
raise 'Incorrect bundle.'
|
raise MalformedBundleException(
|
||||||
|
'All files in the bundle must be inside the activity directory')
|
||||||
|
|
||||||
return bundle_root_dir
|
return bundle_root_dir
|
||||||
|
|
||||||
@ -294,10 +297,7 @@ class Bundle:
|
|||||||
|
|
||||||
self._init_with_path(bundle_path)
|
self._init_with_path(bundle_path)
|
||||||
|
|
||||||
bus = dbus.SessionBus()
|
if not activity.get_registry().add_bundle(bundle_path):
|
||||||
proxy_obj = bus.get_object(_DBUS_SHELL_SERVICE, _DBUS_SHELL_PATH)
|
|
||||||
dbus_service = dbus.Interface(proxy_obj, _DBUS_ACTIVITY_REGISTRY_IFACE)
|
|
||||||
if not dbus_service.AddBundle(bundle_path):
|
|
||||||
raise RegistrationException
|
raise RegistrationException
|
||||||
|
|
||||||
def deinstall(self):
|
def deinstall(self):
|
||||||
|
@ -162,8 +162,7 @@ def _get_mo_list(manifest):
|
|||||||
|
|
||||||
for lang in _get_po_list(manifest).keys():
|
for lang in _get_po_list(manifest).keys():
|
||||||
filename = _get_service_name() + '.mo'
|
filename = _get_service_name() + '.mo'
|
||||||
mo_list.append(os.path.join(_get_source_path(), 'locale',
|
mo_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename))
|
||||||
lang, 'LC_MESSAGES', filename))
|
|
||||||
|
|
||||||
return mo_list
|
return mo_list
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Copyright (C) 2006-2007 Red Hat, Inc.
|
# 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
|
# This library is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
@ -18,29 +19,39 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
|
import gobject
|
||||||
|
|
||||||
_SHELL_SERVICE = "org.laptop.Shell"
|
_ACTIVITY_REGISTRY_SERVICE_NAME = 'org.laptop.ActivityRegistry'
|
||||||
_SHELL_PATH = "/org/laptop/Shell"
|
_ACTIVITY_REGISTRY_IFACE = 'org.laptop.ActivityRegistry'
|
||||||
_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry"
|
_ACTIVITY_REGISTRY_PATH = '/org/laptop/ActivityRegistry'
|
||||||
|
|
||||||
def _activity_info_from_dict(info_dict):
|
def _activity_info_from_dict(info_dict):
|
||||||
if not info_dict:
|
if not info_dict:
|
||||||
return None
|
return None
|
||||||
return ActivityInfo(info_dict['name'], info_dict['icon'],
|
return ActivityInfo(info_dict['name'], info_dict['icon'],
|
||||||
info_dict['service_name'], info_dict['path'])
|
info_dict['service_name'], info_dict['path'],
|
||||||
|
info_dict['show_launcher'])
|
||||||
|
|
||||||
class ActivityInfo(object):
|
class ActivityInfo(object):
|
||||||
def __init__(self, name, icon, service_name, path):
|
def __init__(self, name, icon, service_name, path, show_launcher):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.service_name = service_name
|
self.service_name = service_name
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.show_launcher = show_launcher
|
||||||
|
|
||||||
class ActivityRegistry(object):
|
class ActivityRegistry(gobject.GObject):
|
||||||
|
__gsignals__ = {
|
||||||
|
'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||||
|
([gobject.TYPE_PYOBJECT]))
|
||||||
|
}
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
bus = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
bus_object = bus.get_object(_SHELL_SERVICE, _SHELL_PATH)
|
bus_object = bus.get_object(_ACTIVITY_REGISTRY_SERVICE_NAME,
|
||||||
self._registry = dbus.Interface(bus_object, _REGISTRY_IFACE)
|
_ACTIVITY_REGISTRY_PATH)
|
||||||
|
self._registry = dbus.Interface(bus_object, _ACTIVITY_REGISTRY_IFACE)
|
||||||
self._registry.connect_to_signal('ActivityAdded', self._activity_added_cb)
|
self._registry.connect_to_signal('ActivityAdded', self._activity_added_cb)
|
||||||
|
|
||||||
# Two caches fo saving some travel across dbus.
|
# Two caches fo saving some travel across dbus.
|
||||||
@ -55,6 +66,10 @@ class ActivityRegistry(object):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_activities(self):
|
||||||
|
info_list = self._registry.GetActivities()
|
||||||
|
return self._convert_info_list(info_list)
|
||||||
|
|
||||||
def get_activity(self, service_name):
|
def get_activity(self, service_name):
|
||||||
if self._service_name_to_activity_info.has_key(service_name):
|
if self._service_name_to_activity_info.has_key(service_name):
|
||||||
return self._service_name_to_activity_info[service_name]
|
return self._service_name_to_activity_info[service_name]
|
||||||
@ -79,10 +94,14 @@ class ActivityRegistry(object):
|
|||||||
self._mime_type_to_activities[mime_type] = activities
|
self._mime_type_to_activities[mime_type] = activities
|
||||||
return activities
|
return activities
|
||||||
|
|
||||||
def _activity_added_cb(self, bundle):
|
def add_bundle(self, bundle_path):
|
||||||
|
return self._registry.AddBundle(bundle_path)
|
||||||
|
|
||||||
|
def _activity_added_cb(self, info_dict):
|
||||||
logging.debug('ActivityRegistry._activity_added_cb: flushing caches')
|
logging.debug('ActivityRegistry._activity_added_cb: flushing caches')
|
||||||
self._service_name_to_activity_info.clear()
|
self._service_name_to_activity_info.clear()
|
||||||
self._mime_type_to_activities.clear()
|
self._mime_type_to_activities.clear()
|
||||||
|
self.emit('activity-added', _activity_info_from_dict(info_dict))
|
||||||
|
|
||||||
_registry = None
|
_registry = None
|
||||||
|
|
||||||
|
@ -231,13 +231,13 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
stroke_color = None
|
stroke_color = None
|
||||||
if self._active:
|
if self._active:
|
||||||
if self._fill_color:
|
if self._fill_color:
|
||||||
fill_color = self._fill_color.get_html()
|
fill_color = self._fill_color.get_svg()
|
||||||
if self._stroke_color:
|
if self._stroke_color:
|
||||||
stroke_color = self._stroke_color.get_html()
|
stroke_color = self._stroke_color.get_svg()
|
||||||
else:
|
else:
|
||||||
stroke_color = color.ICON_STROKE_INACTIVE.get_html()
|
stroke_color = color.ICON_STROKE_INACTIVE.get_svg()
|
||||||
if self._fill_color:
|
if self._fill_color:
|
||||||
fill_color = self._fill_color.get_html()
|
fill_color = self._fill_color.get_svg()
|
||||||
return [fill_color, stroke_color]
|
return [fill_color, stroke_color]
|
||||||
|
|
||||||
def _get_handle(self, name, handle):
|
def _get_handle(self, name, handle):
|
||||||
|
@ -53,7 +53,7 @@ class IconButton(CanvasIcon, hippo.CanvasItem):
|
|||||||
if self.props.active:
|
if self.props.active:
|
||||||
self.props.background_color = 0x000000FF
|
self.props.background_color = 0x000000FF
|
||||||
else:
|
else:
|
||||||
self.props.background_color = 0x404040FF
|
self.props.background_color = 0x00000000
|
||||||
|
|
||||||
def _icon_clicked_cb(self, button):
|
def _icon_clicked_cb(self, button):
|
||||||
if self._palette:
|
if self._palette:
|
||||||
|
@ -21,12 +21,12 @@ import time
|
|||||||
import gtk
|
import gtk
|
||||||
import hippo
|
import hippo
|
||||||
|
|
||||||
from sugar.graphics.frame import Frame
|
|
||||||
from sugar.activity.bundle import Bundle
|
from sugar.activity.bundle import Bundle
|
||||||
from sugar.date import Date
|
from sugar.date import Date
|
||||||
from sugar.graphics import style
|
from sugar.graphics import style
|
||||||
from sugar.graphics.canvasicon import CanvasIcon
|
from sugar.graphics.canvasicon import CanvasIcon
|
||||||
from sugar.graphics.xocolor import XoColor
|
from sugar.graphics.xocolor import XoColor
|
||||||
|
from sugar.graphics.canvasroundbox import CanvasRoundBox
|
||||||
from sugar.datastore import datastore
|
from sugar.datastore import datastore
|
||||||
from sugar import activity
|
from sugar import activity
|
||||||
from sugar.objects import objecttype
|
from sugar.objects import objecttype
|
||||||
@ -95,12 +95,12 @@ class ObjectChooser(gtk.Dialog):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class CollapsedEntry(Frame):
|
class CollapsedEntry(CanvasRoundBox):
|
||||||
_DATE_COL_WIDTH = style.zoom(100)
|
_DATE_COL_WIDTH = style.zoom(100)
|
||||||
_BUDDIES_COL_WIDTH = style.zoom(50)
|
_BUDDIES_COL_WIDTH = style.zoom(50)
|
||||||
|
|
||||||
def __init__(self, jobject):
|
def __init__(self, jobject):
|
||||||
Frame.__init__(self)
|
CanvasRoundBox.__init__(self)
|
||||||
self.props.box_height = style.zoom(75)
|
self.props.box_height = style.zoom(75)
|
||||||
self.props.spacing = style.DEFAULT_SPACING
|
self.props.spacing = style.DEFAULT_SPACING
|
||||||
self.props.border_color = style.COLOR_BLACK.get_int()
|
self.props.border_color = style.COLOR_BLACK.get_int()
|
||||||
|
@ -36,7 +36,40 @@ _RIGHT_TOP = 5
|
|||||||
_TOP_LEFT = 6
|
_TOP_LEFT = 6
|
||||||
_TOP_RIGHT = 7
|
_TOP_RIGHT = 7
|
||||||
|
|
||||||
class Palette(gobject.GObject):
|
|
||||||
|
# Helper function to find the gap position and size of widget a
|
||||||
|
def _calculate_gap(a, b):
|
||||||
|
# Test for each side if the palette and invoker are
|
||||||
|
# adjacent to each other.
|
||||||
|
gap = True
|
||||||
|
|
||||||
|
if a.y + a.height == b.y:
|
||||||
|
gap_side = gtk.POS_BOTTOM
|
||||||
|
elif a.x + a.width == b.x:
|
||||||
|
gap_side = gtk.POS_RIGHT
|
||||||
|
elif a.x == b.x + b.width:
|
||||||
|
gap_side = gtk.POS_LEFT
|
||||||
|
elif a.y == b.y + b.height:
|
||||||
|
gap_side = gtk.POS_TOP
|
||||||
|
else:
|
||||||
|
gap = False
|
||||||
|
|
||||||
|
if gap:
|
||||||
|
if gap_side == gtk.POS_BOTTOM or gap_side == gtk.POS_TOP:
|
||||||
|
gap_start = min(a.width, max(0, b.x - a.x))
|
||||||
|
gap_size = max(0, min(a.width,
|
||||||
|
(b.x + b.width) - a.x) - gap_start)
|
||||||
|
elif gap_side == gtk.POS_RIGHT or gap_side == gtk.POS_LEFT:
|
||||||
|
gap_start = min(a.height, max(0, b.y - a.y))
|
||||||
|
gap_size = max(0, min(a.height,
|
||||||
|
(b.y + b.height) - a.y) - gap_start)
|
||||||
|
|
||||||
|
if gap and gap_size > 0:
|
||||||
|
return (gap_side, gap_start, gap_size)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
class Palette(gtk.Window):
|
||||||
DEFAULT = 0
|
DEFAULT = 0
|
||||||
AT_CURSOR = 1
|
AT_CURSOR = 1
|
||||||
AROUND = 2
|
AROUND = 2
|
||||||
@ -54,7 +87,9 @@ class Palette(gobject.GObject):
|
|||||||
'invoker' : (object, None, None,
|
'invoker' : (object, None, None,
|
||||||
gobject.PARAM_READWRITE),
|
gobject.PARAM_READWRITE),
|
||||||
'position' : (gobject.TYPE_INT, None, None, 0, 6,
|
'position' : (gobject.TYPE_INT, None, None, 0, 6,
|
||||||
0, gobject.PARAM_READWRITE)
|
0, gobject.PARAM_READWRITE),
|
||||||
|
'draw-gap' : (bool, None, None, False,
|
||||||
|
gobject.PARAM_READWRITE)
|
||||||
}
|
}
|
||||||
|
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
@ -65,16 +100,21 @@ class Palette(gobject.GObject):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, label, accel_path=None):
|
def __init__(self, label, accel_path=None):
|
||||||
gobject.GObject.__init__(self)
|
gtk.Window.__init__(self)
|
||||||
|
|
||||||
|
self.set_decorated(False)
|
||||||
|
self.set_resizable(False)
|
||||||
|
self.connect('realize', self._realize_cb)
|
||||||
|
|
||||||
self._full_request = [0, 0]
|
self._full_request = [0, 0]
|
||||||
self._cursor_x = 0
|
self._cursor_x = 0
|
||||||
self._cursor_y = 0
|
self._cursor_y = 0
|
||||||
self._state = self._SECONDARY
|
self._state = self._PRIMARY
|
||||||
self._invoker = None
|
self._invoker = None
|
||||||
self._group_id = None
|
self._group_id = None
|
||||||
self._up = False
|
self._up = False
|
||||||
self._position = self.DEFAULT
|
self._position = self.DEFAULT
|
||||||
|
self._draw_gap = False
|
||||||
self._palette_popup_sid = None
|
self._palette_popup_sid = None
|
||||||
|
|
||||||
self._popup_anim = animator.Animator(0.3, 10)
|
self._popup_anim = animator.Animator(0.3, 10)
|
||||||
@ -86,60 +126,70 @@ class Palette(gobject.GObject):
|
|||||||
self._popdown_anim = animator.Animator(0.6, 10)
|
self._popdown_anim = animator.Animator(0.6, 10)
|
||||||
self._popdown_anim.add(_PopdownAnimation(self))
|
self._popdown_anim.add(_PopdownAnimation(self))
|
||||||
|
|
||||||
self._menu = _sugaruiext.Menu()
|
vbox = gtk.VBox()
|
||||||
|
vbox.set_border_width(style.DEFAULT_PADDING)
|
||||||
|
|
||||||
self._primary = _PrimaryMenuItem(label, accel_path)
|
self._label = gtk.Label()
|
||||||
self._menu.append(self._primary)
|
vbox.pack_start(self._label, False)
|
||||||
self._primary.show()
|
|
||||||
|
|
||||||
self._separator = gtk.SeparatorMenuItem()
|
self._secondary_box = gtk.VBox()
|
||||||
self._menu.append(self._separator)
|
vbox.pack_start(self._secondary_box)
|
||||||
|
|
||||||
self._content = _ContentMenuItem()
|
self._separator = gtk.HSeparator()
|
||||||
self._menu.append(self._content)
|
self._secondary_box.pack_start(self._separator)
|
||||||
|
|
||||||
self._button_bar = _ButtonBarMenuItem()
|
self._menu_box = gtk.VBox()
|
||||||
self._menu.append(self._button_bar)
|
self._secondary_box.pack_start(self._menu_box)
|
||||||
|
self._menu_box.show()
|
||||||
|
|
||||||
self._menu.connect('enter-notify-event',
|
self._content = gtk.VBox()
|
||||||
self._enter_notify_event_cb)
|
self._secondary_box.pack_start(self._content)
|
||||||
self._menu.connect('leave-notify-event',
|
self._content.show()
|
||||||
self._leave_notify_event_cb)
|
|
||||||
|
self.action_bar = PaletteActionBar()
|
||||||
|
self._secondary_box.pack_start(self.action_bar)
|
||||||
|
self.action_bar.show()
|
||||||
|
|
||||||
|
self.add(vbox)
|
||||||
|
vbox.show()
|
||||||
|
|
||||||
|
self.menu = _Menu(self)
|
||||||
|
self.menu.show()
|
||||||
|
|
||||||
|
self.connect('enter-notify-event',
|
||||||
|
self._enter_notify_event_cb)
|
||||||
|
self.connect('leave-notify-event',
|
||||||
|
self._leave_notify_event_cb)
|
||||||
|
|
||||||
|
self.set_primary_text(label, accel_path)
|
||||||
|
|
||||||
def is_up(self):
|
def is_up(self):
|
||||||
return self._up
|
return self._up
|
||||||
|
|
||||||
|
def get_rect(self):
|
||||||
|
win_x, win_y = self.window.get_origin()
|
||||||
|
rectangle = self.get_allocation()
|
||||||
|
|
||||||
|
x = win_x + rectangle.x
|
||||||
|
y = win_y + rectangle.y
|
||||||
|
width = rectangle.width
|
||||||
|
height = rectangle.height
|
||||||
|
|
||||||
|
return gtk.gdk.Rectangle(x, y, width, height)
|
||||||
|
|
||||||
def set_primary_text(self, label, accel_path=None):
|
def set_primary_text(self, label, accel_path=None):
|
||||||
self._primary.set_label(label, accel_path)
|
self._label.set_text(label)
|
||||||
|
self._label.show()
|
||||||
def append_menu_item(self, item):
|
|
||||||
self._separator.show()
|
|
||||||
self._menu.insert(item, len(self._menu.get_children()) - 2)
|
|
||||||
|
|
||||||
def insert_menu_item(self, item, index=-1):
|
|
||||||
self._separator.show()
|
|
||||||
if index < 0:
|
|
||||||
self._menu.insert(item, len(self._menu.get_children()) - 2)
|
|
||||||
else:
|
|
||||||
self._menu.insert(item, index + 2)
|
|
||||||
|
|
||||||
def remove_menu_item(self, index):
|
|
||||||
if index > len(self._menu.get_children()) - 4:
|
|
||||||
raise ValueError('index %i out of range' % index)
|
|
||||||
self._menu.remove(self._menu.get_children()[index + 2])
|
|
||||||
if len(self._menu.get_children()) == 0:
|
|
||||||
self._separator.hide()
|
|
||||||
|
|
||||||
def menu_item_count(self):
|
|
||||||
return len(self._menu.get_children()) - 4
|
|
||||||
|
|
||||||
def set_content(self, widget):
|
def set_content(self, widget):
|
||||||
self._content.set_widget(widget)
|
if len(self._content.get_children()) > 0:
|
||||||
self._content.show()
|
self.remove(self._content.get_children()[0])
|
||||||
|
|
||||||
def append_button(self, button):
|
if widget is not None:
|
||||||
self._button_bar.append_button(button)
|
self._content.add(widget)
|
||||||
self._button_bar.show()
|
|
||||||
|
self._update_accept_focus()
|
||||||
|
self._update_separator()
|
||||||
|
|
||||||
def set_group_id(self, group_id):
|
def set_group_id(self, group_id):
|
||||||
if self._group_id:
|
if self._group_id:
|
||||||
@ -154,9 +204,11 @@ class Palette(gobject.GObject):
|
|||||||
self._invoker = value
|
self._invoker = value
|
||||||
self._invoker.connect('mouse-enter', self._invoker_mouse_enter_cb)
|
self._invoker.connect('mouse-enter', self._invoker_mouse_enter_cb)
|
||||||
self._invoker.connect('mouse-leave', self._invoker_mouse_leave_cb)
|
self._invoker.connect('mouse-leave', self._invoker_mouse_leave_cb)
|
||||||
self._invoker.connect('focus-out', self._invoker_focus_out_cb)
|
|
||||||
elif pspec.name == 'position':
|
elif pspec.name == 'position':
|
||||||
self._position = value
|
self._position = value
|
||||||
|
elif pspec.name == 'draw-gap':
|
||||||
|
self._draw_gap = value
|
||||||
|
self.queue_draw()
|
||||||
else:
|
else:
|
||||||
raise AssertionError
|
raise AssertionError
|
||||||
|
|
||||||
@ -165,9 +217,57 @@ class Palette(gobject.GObject):
|
|||||||
return self._invoker
|
return self._invoker
|
||||||
elif pspec.name == 'position':
|
elif pspec.name == 'position':
|
||||||
return self._position
|
return self._position
|
||||||
|
elif pspec.name == 'draw-gap':
|
||||||
|
return self._draw_gap
|
||||||
else:
|
else:
|
||||||
raise AssertionError
|
raise AssertionError
|
||||||
|
|
||||||
|
def do_size_allocate(self, allocation):
|
||||||
|
gtk.Window.do_size_allocate(self, allocation)
|
||||||
|
self.queue_draw()
|
||||||
|
|
||||||
|
def do_expose_event(self, event):
|
||||||
|
# We want to draw a border with a beautiful gap
|
||||||
|
if self._draw_gap:
|
||||||
|
invoker = self._invoker.get_rect()
|
||||||
|
palette = self.get_rect()
|
||||||
|
|
||||||
|
gap = _calculate_gap(palette, invoker)
|
||||||
|
else:
|
||||||
|
gap = False
|
||||||
|
|
||||||
|
if gap:
|
||||||
|
self.style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
|
||||||
|
gtk.SHADOW_IN, event.area, self, "palette",
|
||||||
|
0, 0,
|
||||||
|
self.allocation.width,
|
||||||
|
self.allocation.height,
|
||||||
|
gap[0], gap[1], gap[2])
|
||||||
|
else:
|
||||||
|
self.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||||
|
gtk.SHADOW_IN, event.area, self, "palette",
|
||||||
|
0, 0,
|
||||||
|
self.allocation.width,
|
||||||
|
self.allocation.height)
|
||||||
|
|
||||||
|
# Fall trough to the container expose handler.
|
||||||
|
# (Leaving out the window expose handler which redraws everything)
|
||||||
|
gtk.Bin.do_expose_event(self, event)
|
||||||
|
|
||||||
|
def _update_separator(self):
|
||||||
|
visible = len(self.menu.get_children()) > 0 or \
|
||||||
|
len(self._content.get_children()) > 0
|
||||||
|
self._separator.props.visible = visible
|
||||||
|
|
||||||
|
def _update_accept_focus(self):
|
||||||
|
accept_focus = len(self._content.get_children())
|
||||||
|
if self.window:
|
||||||
|
self.window.set_accept_focus(accept_focus)
|
||||||
|
|
||||||
|
def _realize_cb(self, widget):
|
||||||
|
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||||
|
self._update_accept_focus()
|
||||||
|
|
||||||
def _in_screen(self, x, y):
|
def _in_screen(self, x, y):
|
||||||
[width, height] = self._full_request
|
[width, height] = self._full_request
|
||||||
screen_area = self._invoker.get_screen_area()
|
screen_area = self._invoker.get_screen_area()
|
||||||
@ -182,7 +282,7 @@ class Palette(gobject.GObject):
|
|||||||
if inv_rect == None:
|
if inv_rect == None:
|
||||||
inv_rect = self._invoker.get_rect()
|
inv_rect = self._invoker.get_rect()
|
||||||
|
|
||||||
palette_width, palette_height = self._menu.size_request()
|
palette_width, palette_height = self.size_request()
|
||||||
|
|
||||||
x = inv_rect.x + inv_rect.width * invoker_halign + \
|
x = inv_rect.x + inv_rect.width * invoker_halign + \
|
||||||
palette_width * palette_halign
|
palette_width * palette_halign
|
||||||
@ -241,12 +341,12 @@ class Palette(gobject.GObject):
|
|||||||
def _update_full_request(self):
|
def _update_full_request(self):
|
||||||
state = self._state
|
state = self._state
|
||||||
|
|
||||||
self._menu.set_size_request(-1, -1)
|
self.set_size_request(-1, -1)
|
||||||
|
|
||||||
self._set_state(self._SECONDARY)
|
self._set_state(self._SECONDARY)
|
||||||
self._full_request = self._menu.size_request()
|
self._full_request = self.size_request()
|
||||||
|
|
||||||
self._menu.set_size_request(self._full_request[0], -1)
|
self.set_size_request(self._full_request[0], -1)
|
||||||
|
|
||||||
self._set_state(state)
|
self._set_state(state)
|
||||||
|
|
||||||
@ -282,7 +382,7 @@ class Palette(gobject.GObject):
|
|||||||
elif position == self.TOP:
|
elif position == self.TOP:
|
||||||
x, y = self._get_top_position()
|
x, y = self._get_top_position()
|
||||||
|
|
||||||
self._menu.popup(x, y)
|
self.move(x, y)
|
||||||
|
|
||||||
def _show(self):
|
def _show(self):
|
||||||
if self._up:
|
if self._up:
|
||||||
@ -291,11 +391,12 @@ class Palette(gobject.GObject):
|
|||||||
self._update_cursor_position()
|
self._update_cursor_position()
|
||||||
self._update_full_request()
|
self._update_full_request()
|
||||||
|
|
||||||
self._invoker.connect_to_parent()
|
self._palette_popup_sid = _palette_observer.connect(
|
||||||
self._palette_popup_sid = _palette_observer.connect('popup',
|
'popup', self._palette_observer_popup_cb)
|
||||||
self._palette_observer_popup_cb)
|
|
||||||
|
|
||||||
self._update_position()
|
self._update_position()
|
||||||
|
self.menu.set_active(True)
|
||||||
|
self.show()
|
||||||
|
|
||||||
self._up = True
|
self._up = True
|
||||||
_palette_observer.emit('popup', self)
|
_palette_observer.emit('popup', self)
|
||||||
@ -305,7 +406,8 @@ class Palette(gobject.GObject):
|
|||||||
if not self._palette_popup_sid is None:
|
if not self._palette_popup_sid is None:
|
||||||
_palette_observer.disconnect(self._palette_popup_sid)
|
_palette_observer.disconnect(self._palette_popup_sid)
|
||||||
self._palette_popup_sid = None
|
self._palette_popup_sid = None
|
||||||
self._menu.popdown()
|
self.menu.set_active(False)
|
||||||
|
self.hide()
|
||||||
|
|
||||||
self._up = False
|
self._up = False
|
||||||
self.emit('popdown')
|
self.emit('popdown')
|
||||||
@ -329,25 +431,11 @@ class Palette(gobject.GObject):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if state == self._PRIMARY:
|
if state == self._PRIMARY:
|
||||||
self._primary.show()
|
self.menu.unembed()
|
||||||
for menu_item in self._menu.get_children()[1:]:
|
self._secondary_box.hide()
|
||||||
menu_item.hide()
|
|
||||||
elif state == self._SECONDARY:
|
elif state == self._SECONDARY:
|
||||||
middle_menu_items = self._menu.get_children()
|
self.menu.embed(self._menu_box)
|
||||||
middle_menu_items = middle_menu_items[2:len(middle_menu_items) - 2]
|
self._secondary_box.show()
|
||||||
if middle_menu_items or \
|
|
||||||
not self._content.is_empty() or \
|
|
||||||
not self._button_bar.is_empty():
|
|
||||||
self._separator.show()
|
|
||||||
|
|
||||||
for menu_item in middle_menu_items:
|
|
||||||
menu_item.show()
|
|
||||||
|
|
||||||
if not self._content.is_empty():
|
|
||||||
self._content.show()
|
|
||||||
|
|
||||||
if not self._button_bar.is_empty():
|
|
||||||
self._button_bar.show()
|
|
||||||
|
|
||||||
self._state = state
|
self._state = state
|
||||||
|
|
||||||
@ -357,68 +445,54 @@ class Palette(gobject.GObject):
|
|||||||
def _invoker_mouse_leave_cb(self, invoker):
|
def _invoker_mouse_leave_cb(self, invoker):
|
||||||
self.popdown()
|
self.popdown()
|
||||||
|
|
||||||
def _invoker_focus_out_cb(self, invoker):
|
|
||||||
self._hide()
|
|
||||||
|
|
||||||
def _enter_notify_event_cb(self, widget, event):
|
def _enter_notify_event_cb(self, widget, event):
|
||||||
if event.detail == gtk.gdk.NOTIFY_NONLINEAR:
|
if event.detail != gtk.gdk.NOTIFY_INFERIOR:
|
||||||
self._popdown_anim.stop()
|
self._popdown_anim.stop()
|
||||||
self._secondary_anim.start()
|
self._secondary_anim.start()
|
||||||
|
|
||||||
def _leave_notify_event_cb(self, widget, event):
|
def _leave_notify_event_cb(self, widget, event):
|
||||||
if event.detail == gtk.gdk.NOTIFY_NONLINEAR:
|
if event.detail != gtk.gdk.NOTIFY_INFERIOR:
|
||||||
self.popdown()
|
self.popdown()
|
||||||
|
|
||||||
def _palette_observer_popup_cb(self, observer, palette):
|
def _palette_observer_popup_cb(self, observer, palette):
|
||||||
if self != palette:
|
if self != palette:
|
||||||
self._hide()
|
self._hide()
|
||||||
|
|
||||||
class _PrimaryMenuItem(gtk.MenuItem):
|
class PaletteActionBar(gtk.HButtonBox):
|
||||||
def __init__(self, label, accel_path):
|
def add_action(label, icon_name=None):
|
||||||
gtk.MenuItem.__init__(self)
|
button = Button(label)
|
||||||
self.set_border_width(style.DEFAULT_PADDING)
|
|
||||||
self._set_label(label, accel_path)
|
|
||||||
|
|
||||||
def set_label(self, label, accel_path):
|
if icon_name:
|
||||||
self.remove(self._label)
|
icon = Icon(icon_name)
|
||||||
self._set_label(label, accel_path)
|
button.set_image(icon)
|
||||||
|
icon.show()
|
||||||
|
|
||||||
def _set_label(self, label, accel_path):
|
self.pack_start(button)
|
||||||
self._label = gtk.AccelLabel(label)
|
button.show()
|
||||||
self._label.set_accel_widget(self)
|
|
||||||
|
|
||||||
if accel_path:
|
class _Menu(_sugaruiext.Menu):
|
||||||
self.set_accel_path(accel_path)
|
__gtype_name__ = 'SugarPaletteMenu'
|
||||||
self._label.set_alignment(0.0, 0.5)
|
|
||||||
|
|
||||||
self.add(self._label)
|
def __init__(self, palette):
|
||||||
self._label.show()
|
_sugaruiext.Menu.__init__(self)
|
||||||
|
self._palette = palette
|
||||||
|
|
||||||
class _ContentMenuItem(gtk.MenuItem):
|
def do_insert(self, item, position):
|
||||||
def __init__(self):
|
_sugaruiext.Menu.do_insert(self, item, position)
|
||||||
gtk.MenuItem.__init__(self)
|
self._palette._update_separator()
|
||||||
|
|
||||||
def set_widget(self, widget):
|
def do_expose_event(self, event):
|
||||||
if self.child:
|
# Ignore the Menu expose, just do the MenuShell expose to prevent any
|
||||||
self.remove(self.child)
|
# border from being drawn here. A border is drawn by the palette object
|
||||||
self.add(widget)
|
# around everything.
|
||||||
|
gtk.MenuShell.do_expose_event(self, event)
|
||||||
|
|
||||||
def is_empty(self):
|
def do_grab_notify(self, was_grabbed):
|
||||||
return self.child is None or not self.child.props.visible
|
# Ignore grab_notify as the menu would close otherwise
|
||||||
|
pass
|
||||||
|
|
||||||
class _ButtonBarMenuItem(gtk.MenuItem):
|
def do_deactivate(self):
|
||||||
def __init__(self):
|
self._palette._hide()
|
||||||
gtk.MenuItem.__init__(self)
|
|
||||||
|
|
||||||
self._hbar = gtk.HButtonBox()
|
|
||||||
self.add(self._hbar)
|
|
||||||
self._hbar.show()
|
|
||||||
|
|
||||||
def append_button(self, button):
|
|
||||||
self._hbar.pack_start(button)
|
|
||||||
|
|
||||||
def is_empty(self):
|
|
||||||
return len(self._hbar.get_children()) == 0
|
|
||||||
|
|
||||||
class _PopupAnimation(animator.Animation):
|
class _PopupAnimation(animator.Animation):
|
||||||
def __init__(self, palette):
|
def __init__(self, palette):
|
||||||
@ -469,13 +543,6 @@ class Invoker(gobject.GObject):
|
|||||||
height = gtk.gdk.screen_height()
|
height = gtk.gdk.screen_height()
|
||||||
return gtk.gdk.Rectangle(0, 0, width, height)
|
return gtk.gdk.Rectangle(0, 0, width, height)
|
||||||
|
|
||||||
def connect_to_parent(self):
|
|
||||||
window = self.get_toplevel()
|
|
||||||
window.connect('focus-out-event', self._window_focus_out_event_cb)
|
|
||||||
|
|
||||||
def _window_focus_out_event_cb(self, widget, event):
|
|
||||||
self.emit('focus-out')
|
|
||||||
|
|
||||||
class WidgetInvoker(Invoker):
|
class WidgetInvoker(Invoker):
|
||||||
def __init__(self, widget):
|
def __init__(self, widget):
|
||||||
Invoker.__init__(self)
|
Invoker.__init__(self)
|
||||||
@ -495,6 +562,37 @@ class WidgetInvoker(Invoker):
|
|||||||
|
|
||||||
return gtk.gdk.Rectangle(x, y, width, height)
|
return gtk.gdk.Rectangle(x, y, width, height)
|
||||||
|
|
||||||
|
def draw_invoker_rect(self, event, palette):
|
||||||
|
style = self._widget.style
|
||||||
|
if palette.is_up():
|
||||||
|
gap = _calculate_gap(self.get_rect(), palette.get_rect())
|
||||||
|
|
||||||
|
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,
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||||
|
gtk.SHADOW_NONE, event.area, self._widget,
|
||||||
|
"palette-invoker",
|
||||||
|
self._widget.allocation.x,
|
||||||
|
self._widget.allocation.y,
|
||||||
|
self._widget.allocation.width,
|
||||||
|
self._widget.allocation.height)
|
||||||
|
|
||||||
def _enter_notify_event_cb(self, widget, event):
|
def _enter_notify_event_cb(self, widget, event):
|
||||||
self.emit('mouse-enter')
|
self.emit('mouse-enter')
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ from sugar.graphics.icon import Icon
|
|||||||
from sugar.graphics.palette import Palette, WidgetInvoker
|
from sugar.graphics.palette import Palette, WidgetInvoker
|
||||||
|
|
||||||
class RadioToolButton(gtk.RadioToolButton):
|
class RadioToolButton(gtk.RadioToolButton):
|
||||||
|
__gtype_name__ = "SugarRadioToolButton"
|
||||||
|
|
||||||
def __init__(self, named_icon=None, group=None):
|
def __init__(self, named_icon=None, group=None):
|
||||||
gtk.RadioToolButton.__init__(self, group=group)
|
gtk.RadioToolButton.__init__(self, group=group)
|
||||||
self._palette = None
|
self._palette = None
|
||||||
@ -38,9 +40,25 @@ class RadioToolButton(gtk.RadioToolButton):
|
|||||||
def set_palette(self, palette):
|
def set_palette(self, palette):
|
||||||
self._palette = palette
|
self._palette = palette
|
||||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
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):
|
def set_tooltip(self, text):
|
||||||
self._palette = Palette(text)
|
self._palette = Palette(text)
|
||||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
palette = property(get_palette, set_palette)
|
||||||
|
@ -81,6 +81,12 @@ class Color(object):
|
|||||||
|
|
||||||
return (r, g, b)
|
return (r, g, b)
|
||||||
|
|
||||||
|
def get_svg(self):
|
||||||
|
if self._a == 0.0:
|
||||||
|
return 'none'
|
||||||
|
else:
|
||||||
|
return self.get_html()
|
||||||
|
|
||||||
_XO_DPI = 200.0
|
_XO_DPI = 200.0
|
||||||
|
|
||||||
_FOCUS_LINE_WIDTH = 2
|
_FOCUS_LINE_WIDTH = 2
|
||||||
@ -113,6 +119,7 @@ TOOLBOX_TAB_LABEL_WIDTH = zoom(150 - 15 * 2)
|
|||||||
|
|
||||||
COLOR_BLACK = Color('#000000')
|
COLOR_BLACK = Color('#000000')
|
||||||
COLOR_WHITE = Color('#FFFFFF')
|
COLOR_WHITE = Color('#FFFFFF')
|
||||||
|
COLOR_TRANSPARENT = Color('#FFFFFF', alpha=0.0)
|
||||||
COLOR_PANEL_GREY = Color('#C0C0C0')
|
COLOR_PANEL_GREY = Color('#C0C0C0')
|
||||||
COLOR_SELECTION_GREY = Color('#A6A6A6')
|
COLOR_SELECTION_GREY = Color('#A6A6A6')
|
||||||
COLOR_INACTIVE_FILL = Color('#9D9FA1')
|
COLOR_INACTIVE_FILL = Color('#9D9FA1')
|
||||||
|
@ -21,6 +21,8 @@ from sugar.graphics.icon import Icon
|
|||||||
from sugar.graphics.palette import Palette, WidgetInvoker
|
from sugar.graphics.palette import Palette, WidgetInvoker
|
||||||
|
|
||||||
class ToggleToolButton(gtk.ToggleToolButton):
|
class ToggleToolButton(gtk.ToggleToolButton):
|
||||||
|
__gtype_name__ = "SugarToggleToolButton"
|
||||||
|
|
||||||
def __init__(self, named_icon=None):
|
def __init__(self, named_icon=None):
|
||||||
gtk.ToggleToolButton.__init__(self)
|
gtk.ToggleToolButton.__init__(self)
|
||||||
self._palette = None
|
self._palette = None
|
||||||
@ -37,9 +39,25 @@ class ToggleToolButton(gtk.ToggleToolButton):
|
|||||||
def set_palette(self, palette):
|
def set_palette(self, palette):
|
||||||
self._palette = palette
|
self._palette = palette
|
||||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
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):
|
def set_tooltip(self, text):
|
||||||
self._palette = Palette(text)
|
self._palette = Palette(text)
|
||||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
palette = property(get_palette, set_palette)
|
||||||
|
@ -23,6 +23,7 @@ from sugar.graphics.icon import Icon
|
|||||||
from sugar.graphics.palette import Palette, WidgetInvoker
|
from sugar.graphics.palette import Palette, WidgetInvoker
|
||||||
|
|
||||||
class ToolButton(gtk.ToolButton):
|
class ToolButton(gtk.ToolButton):
|
||||||
|
__gtype_name__ = "SugarToolButton"
|
||||||
|
|
||||||
def __init__(self, icon_name=None):
|
def __init__(self, icon_name=None):
|
||||||
gtk.ToolButton.__init__(self)
|
gtk.ToolButton.__init__(self)
|
||||||
@ -41,12 +42,28 @@ class ToolButton(gtk.ToolButton):
|
|||||||
def set_palette(self, palette):
|
def set_palette(self, palette):
|
||||||
self._palette = palette
|
self._palette = palette
|
||||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
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):
|
def set_tooltip(self, text):
|
||||||
self.set_palette(Palette(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)
|
||||||
|
|
||||||
|
gtk.ToolButton.do_expose_event(self, event)
|
||||||
|
|
||||||
def _button_clicked_cb(self, widget):
|
def _button_clicked_cb(self, widget):
|
||||||
if self._palette:
|
if self._palette:
|
||||||
self._palette.popdown(True)
|
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)
|
palette = property(get_palette, set_palette)
|
||||||
|
Loading…
Reference in New Issue
Block a user