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/POTFILES
|
||||
po/*.gmo
|
||||
po/.intltool-merge-cache
|
||||
sugar/__installed__.py
|
||||
tools/sugar-setup-activity
|
||||
threadframe
|
||||
@ -53,4 +54,5 @@ browser/sugar-marshal.h
|
||||
bin/sugar
|
||||
shell/extensions/_extensions.c
|
||||
data/sugar.gtkrc
|
||||
data/sugar.xml
|
||||
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)
|
||||
* #2475 Retrieve correctly the file path for files in removable devices. (tomeu)
|
||||
* #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(LIB, gtk+-2.0)
|
||||
PKG_CHECK_MODULES(LIB_BINDINGS, pygtk-2.0)
|
||||
PKG_CHECK_MODULES(LIBUI, gtk+-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`
|
||||
AC_SUBST(PYGTK_DEFSDIR)
|
||||
@ -45,7 +47,7 @@ data/Makefile
|
||||
lib/Makefile
|
||||
lib/ui/Makefile
|
||||
services/Makefile
|
||||
services/clipboard/Makefile
|
||||
services/shell/Makefile
|
||||
shell/Makefile
|
||||
shell/intro/Makefile
|
||||
shell/hardware/Makefile
|
||||
@ -64,7 +66,7 @@ services/console/Makefile
|
||||
services/console/interface/Makefile
|
||||
services/console/interface/xo/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/memphis_init/Makefile
|
||||
services/console/interface/memphis/plugins/cpu/Makefile
|
||||
|
@ -14,5 +14,23 @@ GTKRC_FILES = \
|
||||
sugar.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)
|
||||
|
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 = \
|
||||
$(LIB_CFLAGS)
|
||||
$(LIBUI_CFLAGS)
|
||||
|
||||
noinst_LTLIBRARIES = libsugarui.la
|
||||
|
||||
libsugarui_la_LIBADD = \
|
||||
$(LIB_LIBS)
|
||||
$(LIBUI_LIBS)
|
||||
|
||||
libsugarui_la_SOURCES = \
|
||||
$(BUILT_SOURCES) \
|
||||
|
@ -28,57 +28,36 @@ static void sugar_menu_init (SugarMenu *menu);
|
||||
G_DEFINE_TYPE(SugarMenu, sugar_menu, GTK_TYPE_MENU)
|
||||
|
||||
void
|
||||
sugar_menu_popup(SugarMenu *menu,
|
||||
int x,
|
||||
int y)
|
||||
sugar_menu_set_active(SugarMenu *menu, gboolean active)
|
||||
{
|
||||
GtkWidget *window;
|
||||
|
||||
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);
|
||||
GTK_MENU_SHELL(menu)->active = active;
|
||||
}
|
||||
|
||||
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;
|
||||
g_return_if_fail(window != NULL);
|
||||
|
||||
GTK_MENU_SHELL(menu)->active = FALSE;
|
||||
|
||||
gtk_widget_hide(GTK_WIDGET(menu));
|
||||
gtk_widget_hide(window);
|
||||
GTK_MENU(menu)->toplevel = gtk_widget_get_toplevel(GTK_WIDGET(parent));
|
||||
gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(parent));
|
||||
}
|
||||
|
||||
static void
|
||||
sugar_menu_size_request (GtkWidget *widget,
|
||||
GtkRequisition *requisition)
|
||||
void
|
||||
sugar_menu_unembed(SugarMenu *menu)
|
||||
{
|
||||
SugarMenu *menu = SUGAR_MENU(widget);
|
||||
|
||||
(* GTK_WIDGET_CLASS (sugar_menu_parent_class)->size_request) (widget, requisition);
|
||||
|
||||
requisition->width = MAX(requisition->width, menu->min_width);
|
||||
if (menu->orig_toplevel) {
|
||||
GTK_MENU(menu)->toplevel = menu->orig_toplevel;
|
||||
gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(menu->orig_toplevel));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
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
|
||||
sugar_menu_init(SugarMenu *menu)
|
||||
{
|
||||
menu->orig_toplevel = NULL;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ typedef struct _SugarMenuClass SugarMenuClass;
|
||||
struct _SugarMenu {
|
||||
GtkMenu base_instance;
|
||||
|
||||
GtkWidget *orig_toplevel;
|
||||
int min_width;
|
||||
};
|
||||
|
||||
@ -45,12 +46,10 @@ struct _SugarMenuClass {
|
||||
};
|
||||
|
||||
GType sugar_menu_get_type (void);
|
||||
void sugar_menu_popup (SugarMenu *menu,
|
||||
int x,
|
||||
int y);
|
||||
void sugar_menu_set_min_width (SugarMenu *menu,
|
||||
int min_width);
|
||||
void sugar_menu_popdown (SugarMenu *menu);
|
||||
void sugar_menu_set_active (SugarMenu *menu,
|
||||
gboolean active);
|
||||
void sugar_menu_embed (SugarMenu *menu,
|
||||
GtkContainer *parent);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -1 +1 @@
|
||||
SUBDIRS = clipboard console
|
||||
SUBDIRS = shell console
|
||||
|
@ -28,29 +28,29 @@ import pango
|
||||
from sugar import env
|
||||
|
||||
class MultiLogView(gtk.VBox):
|
||||
def __init__(self, path):
|
||||
def __init__(self, path, extra_files):
|
||||
self._active_log = None
|
||||
self._iters = []
|
||||
self._extra_files = extra_files
|
||||
|
||||
# Creating Main treeview with Actitivities list
|
||||
tv_menu = gtk.TreeView()
|
||||
tv_menu.connect('cursor-changed', self._load_log)
|
||||
tv_menu.set_rules_hint(True)
|
||||
self._tv_menu = gtk.TreeView()
|
||||
self._tv_menu.connect('cursor-changed', self._load_log)
|
||||
self._tv_menu.set_rules_hint(True)
|
||||
|
||||
# Set width
|
||||
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)
|
||||
tv_menu.set_model(self.store_menu)
|
||||
self._store_menu = gtk.TreeStore(str)
|
||||
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._activity = {}
|
||||
|
||||
# Activities menu
|
||||
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
|
||||
self._view = LogView()
|
||||
@ -59,33 +59,44 @@ class MultiLogView(gtk.VBox):
|
||||
self.hbox.pack_start(self._view, True, True, 0)
|
||||
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)
|
||||
def _load_log(self, treeview):
|
||||
treeselection = treeview.get_selection()
|
||||
|
||||
treestore, iter = treeselection.get_selected()
|
||||
|
||||
# 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
|
||||
self._view.textview.set_buffer(self._activity[act_log])
|
||||
self._view.textview.scroll_to_mark(self._activity[act_log].get_insert(), 0);
|
||||
self._active_log = act_log
|
||||
|
||||
def _update(self, tv_menu):
|
||||
def _update(self):
|
||||
# Searching log files
|
||||
for logfile in os.listdir(self._logs_path):
|
||||
full_log_path = os.path.join(self._logs_path, logfile)
|
||||
self._add_log_file(full_log_path)
|
||||
|
||||
if os.path.isdir(full_log_path):
|
||||
continue
|
||||
for ext in self._extra_files:
|
||||
self._add_log_file(ext)
|
||||
|
||||
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(full_log_path)
|
||||
model = LogBuffer(path)
|
||||
self._activity[logfile] = model
|
||||
|
||||
self._activity[logfile].update()
|
||||
@ -94,17 +105,16 @@ class MultiLogView(gtk.VBox):
|
||||
# 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)
|
||||
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);
|
||||
self._view.textview.scroll_to_mark(self._activity[logfile].get_insert(), 0)
|
||||
|
||||
return True
|
||||
|
||||
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)
|
||||
def _add_column(self, treeview, column_name, index):
|
||||
@ -171,9 +181,20 @@ class LogView(gtk.ScrolledWindow):
|
||||
self.textview.show()
|
||||
|
||||
class Interface:
|
||||
|
||||
def __init__(self):
|
||||
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
|
||||
|
||||
# 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)
|
||||
|
||||
def _start_memphis(self, button):
|
||||
|
||||
# Update information every 1.5 second
|
||||
button.hide()
|
||||
self.interface.button_stop.show()
|
||||
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):
|
||||
|
||||
gobject.source_remove(self._gid)
|
||||
self._running_status = False
|
||||
button.hide()
|
||||
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
|
||||
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']]
|
||||
|
||||
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):
|
||||
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
|
||||
return [dirty_sizes['private']]
|
||||
|
||||
def get_dirty(pself, pid):
|
||||
def get_data(pself, 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._configure_vte()
|
||||
self._vte.set_size(30, 5)
|
||||
self._vte.set_size(100, 5)
|
||||
self._vte.set_size_request(200, 450)
|
||||
self._vte.show()
|
||||
self.pack_start(self._vte)
|
||||
|
@ -7,20 +7,22 @@ class Analysis:
|
||||
def __init__(self, pid):
|
||||
self.pid = pid
|
||||
|
||||
def DirtyRSS(self):
|
||||
def SMaps(self):
|
||||
smaps = proc_smaps.ProcSmaps(self.pid)
|
||||
dirty = []
|
||||
|
||||
private = 0
|
||||
shared = 0
|
||||
private_dirty = 0
|
||||
shared_dirty = 0
|
||||
referenced = 0
|
||||
|
||||
for map in smaps.mappings:
|
||||
private += map.private_dirty
|
||||
shared += map.shared_dirty
|
||||
private_dirty += map.private_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):
|
||||
maps = proc_smaps.ProcMaps(self.pid)
|
||||
|
@ -1,4 +1,6 @@
|
||||
import sys, os
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import string
|
||||
|
||||
class ProcInfo:
|
||||
@ -36,10 +38,12 @@ class ProcInfo:
|
||||
return None
|
||||
|
||||
# Parsing data , check 'man 5 proc' for details
|
||||
data = infile.read().split()
|
||||
|
||||
stat_data = infile.read()
|
||||
infile.close()
|
||||
|
||||
process_name = self._get_process_name(stat_data)
|
||||
data = self._get_safe_split(stat_data)
|
||||
|
||||
state_dic = {
|
||||
'R': 'Running',
|
||||
'S': 'Sleeping',
|
||||
@ -51,10 +55,9 @@ class ProcInfo:
|
||||
|
||||
# user and group owners
|
||||
pidstat = os.stat(pidfile)
|
||||
|
||||
info = {
|
||||
'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_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc
|
||||
'ppid': int(data[3]), # Parent process ID
|
||||
@ -69,6 +72,14 @@ class ProcInfo:
|
||||
|
||||
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
|
||||
def get_CPU_usage(self, cpu_hz, used_jiffies, start_time):
|
||||
|
@ -36,6 +36,7 @@ class ProcSmaps:
|
||||
# Shared_Dirty: 0 kB
|
||||
# Private_Clean: 8 kB
|
||||
# Private_Dirty: 0 kB
|
||||
# Referenced: 4 kb -> Introduced in kernel 2.6.22
|
||||
|
||||
while num_lines > 0:
|
||||
fields = lines[line_idx].split (" ", 5)
|
||||
@ -51,13 +52,20 @@ class ProcSmaps:
|
||||
shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4])
|
||||
private_clean = self.parse_smaps_size_line (lines[line_idx + 5])
|
||||
private_dirty = self.parse_smaps_size_line (lines[line_idx + 6])
|
||||
referenced = self.parse_smaps_size_line (lines[line_idx + 7])
|
||||
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)
|
||||
|
||||
num_lines -= 7
|
||||
line_idx += 7
|
||||
num_lines -= 8
|
||||
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
|
||||
def parse_smaps_size_line (self, line):
|
||||
@ -66,13 +74,15 @@ class ProcSmaps:
|
||||
return int(fields[1])
|
||||
|
||||
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.rss = rss
|
||||
self.shared_clean = shared_clean
|
||||
self.shared_dirty = shared_dirty
|
||||
self.private_clean = private_clean
|
||||
self.private_dirty = private_dirty
|
||||
self.referenced = referenced
|
||||
self.permissions = permissions
|
||||
self.name = name
|
||||
|
||||
|
@ -6,7 +6,15 @@ pygtk.require('2.0')
|
||||
import os
|
||||
import sys
|
||||
from sugar import env
|
||||
from sugar import util
|
||||
|
||||
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
|
||||
|
@ -1,29 +1,37 @@
|
||||
servicedir = $(datadir)/dbus-1/services
|
||||
|
||||
service_in_files = \
|
||||
org.laptop.ActivityRegistry.service.in \
|
||||
org.laptop.Clipboard.service.in \
|
||||
org.laptop.ObjectTypeRegistry.service.in
|
||||
|
||||
service_DATA = \
|
||||
org.laptop.ActivityRegistry.service \
|
||||
org.laptop.Clipboard.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
|
||||
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
||||
|
||||
org.laptop.ObjectTypeRegistry.service: org.laptop.ObjectTypeRegistry.service.in Makefile
|
||||
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
||||
|
||||
sugardir = $(pkgdatadir)/services/clipboard
|
||||
sugardir = $(pkgdatadir)/services/shell
|
||||
|
||||
sugar_PYTHON = \
|
||||
__init__.py \
|
||||
activityregistryservice.py \
|
||||
bundleregistry.py \
|
||||
clipboardobject.py \
|
||||
clipboardservice.py \
|
||||
objecttypeservice.py
|
||||
|
||||
bin_SCRIPTS = sugar-clipboard
|
||||
bin_SCRIPTS = sugar-shell-service
|
||||
|
||||
DISTCLEANFILES = $(service_DATA)
|
||||
|
||||
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:
|
||||
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():
|
||||
return _bundle_registry
|
||||
|
@ -19,9 +19,9 @@ import logging
|
||||
import urlparse
|
||||
|
||||
from sugar.objects import mime
|
||||
from sugar import activity
|
||||
|
||||
import objecttypeservice
|
||||
import bundleregistry
|
||||
|
||||
class ClipboardObject:
|
||||
|
||||
@ -66,30 +66,15 @@ class ClipboardObject:
|
||||
return ''
|
||||
|
||||
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()
|
||||
if not mime:
|
||||
return ''
|
||||
"""
|
||||
registry = activity.get_registry()
|
||||
|
||||
registry = bundleregistry.get_registry()
|
||||
activities = registry.get_activities_for_type(self.get_mime_type())
|
||||
# TODO: should we return several activities?
|
||||
if activities:
|
||||
return activities[0]
|
||||
else:
|
||||
return ''
|
||||
"""
|
||||
if mapping.has_key(mime):
|
||||
return mapping[mime]
|
||||
return activities[0].get_service_name()
|
||||
else:
|
||||
return ''
|
||||
|
||||
@ -101,8 +86,6 @@ class ClipboardObject:
|
||||
|
||||
def add_format(self, 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):
|
||||
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]
|
||||
Name = org.laptop.Clipboard
|
||||
Exec = @bindir@/sugar-clipboard
|
||||
Exec = @bindir@/sugar-shell-service
|
||||
|
@ -1,4 +1,4 @@
|
||||
[D-BUS Service]
|
||||
Name = org.laptop.ObjectTypeRegistry
|
||||
Exec = @bindir@/sugar-clipboard
|
||||
Exec = @bindir@/sugar-shell-service
|
||||
|
@ -23,28 +23,31 @@ import os
|
||||
import logging
|
||||
|
||||
from sugar import logger
|
||||
logger.start('clipboard')
|
||||
logger.start('shellservice')
|
||||
|
||||
import gobject
|
||||
import dbus.glib
|
||||
|
||||
from sugar import env
|
||||
|
||||
sys.path.append(env.get_service_path('clipboard'))
|
||||
sys.path.append(env.get_service_path('shell'))
|
||||
|
||||
import clipboardservice
|
||||
import objecttypeservice
|
||||
import activityregistryservice
|
||||
|
||||
logging.info('Starting clipboard service.')
|
||||
logging.info('Starting shell service.')
|
||||
|
||||
gobject.threads_init()
|
||||
dbus.glib.threads_init()
|
||||
|
||||
clipboard_service = clipboardservice.get_instance()
|
||||
object_type_registry = objecttypeservice.get_instance()
|
||||
activity_registry = activityregistryservice.get_instance()
|
||||
|
||||
loop = gobject.MainLoop()
|
||||
try:
|
||||
loop.run()
|
||||
except KeyboardInterrupt:
|
||||
print 'Ctrl+C pressed, exiting...'
|
||||
|
@ -4,7 +4,6 @@ sugardir = $(pkgdatadir)/shell/model
|
||||
sugar_PYTHON = \
|
||||
__init__.py \
|
||||
accesspointmodel.py \
|
||||
bundleregistry.py \
|
||||
BuddyModel.py \
|
||||
Friends.py \
|
||||
Invites.py \
|
||||
|
@ -18,29 +18,29 @@ import gobject
|
||||
|
||||
from sugar.graphics.xocolor import XoColor
|
||||
from sugar.presence import presenceservice
|
||||
from sugar import activity
|
||||
|
||||
from model import bundleregistry
|
||||
from model.BuddyModel import BuddyModel
|
||||
from model.accesspointmodel import AccessPointModel
|
||||
from hardware import hardwaremanager
|
||||
from hardware import nmclient
|
||||
|
||||
class ActivityModel:
|
||||
def __init__(self, activity, bundle):
|
||||
def __init__(self, activity, activity_info):
|
||||
self._activity = activity
|
||||
self._bundle = bundle
|
||||
self._activity_info = activity_info
|
||||
|
||||
def get_id(self):
|
||||
return self._activity.props.id
|
||||
|
||||
def get_icon_name(self):
|
||||
return self._bundle.get_icon()
|
||||
return self._activity_info.icon
|
||||
|
||||
def get_color(self):
|
||||
return XoColor(self._activity.props.color)
|
||||
|
||||
def get_service_name(self):
|
||||
return self._bundle.get_service_name()
|
||||
return self._activity_info.service_name
|
||||
|
||||
def get_title(self):
|
||||
return self._activity.props.name
|
||||
@ -75,7 +75,6 @@ class MeshModel(gobject.GObject):
|
||||
self._buddies = {}
|
||||
self._access_points = {}
|
||||
self._mesh = None
|
||||
self._bundle_registry = bundleregistry.get_registry()
|
||||
|
||||
self._pservice = presenceservice.get_instance()
|
||||
self._pservice.connect("activity-appeared",
|
||||
@ -196,13 +195,14 @@ class MeshModel(gobject.GObject):
|
||||
def _activity_appeared_cb(self, pservice, activity):
|
||||
self._check_activity(activity)
|
||||
|
||||
def _check_activity(self, activity):
|
||||
bundle = self._bundle_registry.get_bundle(activity.props.type)
|
||||
if not bundle:
|
||||
def _check_activity(self, presence_activity):
|
||||
registry = activity.get_registry()
|
||||
activity_info = registry.get_activity(presence_activity.props.type)
|
||||
if not activity_info:
|
||||
return
|
||||
if self.has_activity(activity.props.id):
|
||||
if self.has_activity(presence_activity.props.id):
|
||||
return
|
||||
self.add_activity(bundle, activity)
|
||||
self.add_activity(activity_info, presence_activity)
|
||||
|
||||
def has_activity(self, activity_id):
|
||||
return self._activities.has_key(activity_id)
|
||||
@ -213,8 +213,8 @@ class MeshModel(gobject.GObject):
|
||||
else:
|
||||
return None
|
||||
|
||||
def add_activity(self, bundle, activity):
|
||||
model = ActivityModel(activity, bundle)
|
||||
def add_activity(self, activity_info, activity):
|
||||
model = ActivityModel(activity, activity_info)
|
||||
self._activities[model.get_id()] = model
|
||||
self.emit('activity-added', model)
|
||||
|
||||
|
@ -44,10 +44,10 @@ class HomeActivity(gobject.GObject):
|
||||
gobject.PARAM_READWRITE),
|
||||
}
|
||||
|
||||
def __init__(self, bundle, activity_id):
|
||||
def __init__(self, activity_info, activity_id):
|
||||
"""Initialise the HomeActivity
|
||||
|
||||
bundle -- sugar.activity.bundle.Bundle instance,
|
||||
activity_info -- sugar.activity.registry.ActivityInfo instance,
|
||||
provides the information required to actually
|
||||
create the new instance. This is, in effect,
|
||||
the "type" of activity being created.
|
||||
@ -61,7 +61,7 @@ class HomeActivity(gobject.GObject):
|
||||
self._pid = None
|
||||
self._service = None
|
||||
self._activity_id = activity_id
|
||||
self._bundle = bundle
|
||||
self._activity_info = activity_info
|
||||
self._launch_time = time.time()
|
||||
self._launching = False
|
||||
|
||||
@ -99,9 +99,9 @@ class HomeActivity(gobject.GObject):
|
||||
return self._window.get_name()
|
||||
|
||||
def get_icon_name(self):
|
||||
"""Retrieve the bundle's icon (file) name"""
|
||||
if self._bundle:
|
||||
return self._bundle.get_icon()
|
||||
"""Retrieve the activity's icon (file) name"""
|
||||
if self._activity_info:
|
||||
return self._activity_info.icon
|
||||
else:
|
||||
return 'theme:stock-missing'
|
||||
|
||||
@ -156,9 +156,9 @@ class HomeActivity(gobject.GObject):
|
||||
return self._window
|
||||
|
||||
def get_type(self):
|
||||
"""Retrieve bundle's "service_name" for future reference"""
|
||||
if self._bundle:
|
||||
return self._bundle.get_service_name()
|
||||
"""Retrieve activity_info's "service_name" for future reference"""
|
||||
if self._activity_info:
|
||||
return self._activity_info.service_name
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -21,9 +21,9 @@ import wnck
|
||||
import dbus
|
||||
|
||||
from sugar import wm
|
||||
from sugar import activity
|
||||
|
||||
from model.homeactivity import HomeActivity
|
||||
from model import bundleregistry
|
||||
|
||||
class HomeModel(gobject.GObject):
|
||||
"""Model of the "Home" view (activity management)
|
||||
@ -51,6 +51,9 @@ class HomeModel(gobject.GObject):
|
||||
([gobject.TYPE_PYOBJECT])),
|
||||
'active-activity-changed': (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE,
|
||||
([gobject.TYPE_PYOBJECT])),
|
||||
'pending-activity-changed': (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE,
|
||||
([gobject.TYPE_PYOBJECT]))
|
||||
}
|
||||
|
||||
@ -58,8 +61,8 @@ class HomeModel(gobject.GObject):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self._activities = []
|
||||
self._bundle_registry = bundleregistry.get_registry()
|
||||
self._current_activity = None
|
||||
self._active_activity = None
|
||||
self._pending_activity = None
|
||||
|
||||
screen = wnck.screen_get_default()
|
||||
screen.connect('window-opened', self._window_opened_cb)
|
||||
@ -67,8 +70,55 @@ class HomeModel(gobject.GObject):
|
||||
screen.connect('active-window-changed',
|
||||
self._active_window_changed_cb)
|
||||
|
||||
def get_current_activity(self):
|
||||
return self._current_activity
|
||||
def get_pending_activity(self):
|
||||
"""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):
|
||||
return iter(self._activities)
|
||||
@ -84,65 +134,48 @@ class HomeModel(gobject.GObject):
|
||||
|
||||
def _window_opened_cb(self, screen, window):
|
||||
if window.get_window_type() == wnck.WINDOW_NORMAL:
|
||||
activity = None
|
||||
home_activity = None
|
||||
|
||||
activity_id = wm.get_activity_id(window)
|
||||
|
||||
bundle_id = wm.get_bundle_id(window)
|
||||
if bundle_id:
|
||||
bundle = self._bundle_registry.get_bundle(bundle_id)
|
||||
service_name = wm.get_bundle_id(window)
|
||||
if service_name:
|
||||
registry = activity.get_registry()
|
||||
activity_info = registry.get_activity(service_name)
|
||||
else:
|
||||
bundle = None
|
||||
activity_info = None
|
||||
|
||||
if activity_id:
|
||||
activity = self._get_activity_by_id(activity_id)
|
||||
home_activity = self._get_activity_by_id(activity_id)
|
||||
|
||||
if not activity:
|
||||
activity = HomeActivity(bundle, activity_id)
|
||||
self._add_activity(activity)
|
||||
if not home_activity:
|
||||
home_activity = HomeActivity(activity_info, activity_id)
|
||||
self._add_activity(home_activity)
|
||||
|
||||
activity.set_window(window)
|
||||
home_activity.set_window(window)
|
||||
|
||||
activity.props.launching = False
|
||||
self.emit('activity-started', activity)
|
||||
home_activity.props.launching = False
|
||||
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):
|
||||
if window.get_window_type() == wnck.WINDOW_NORMAL:
|
||||
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):
|
||||
for activity in self._activities:
|
||||
if activity.get_xid() == xid:
|
||||
return activity
|
||||
for home_activity in self._activities:
|
||||
if home_activity.get_xid() == xid:
|
||||
return home_activity
|
||||
return None
|
||||
|
||||
def _get_activity_by_id(self, activity_id):
|
||||
for activity in self._activities:
|
||||
if activity.get_activity_id() == activity_id:
|
||||
return activity
|
||||
for home_activity in self._activities:
|
||||
if home_activity.get_activity_id() == activity_id:
|
||||
return home_activity
|
||||
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):
|
||||
pass
|
||||
|
||||
@ -151,55 +184,58 @@ class HomeModel(gobject.GObject):
|
||||
|
||||
def _active_window_changed_cb(self, screen):
|
||||
window = screen.get_active_window()
|
||||
if window == None:
|
||||
self.emit('active-activity-changed', None)
|
||||
self._notify_activity_activation(self._current_activity, None)
|
||||
return
|
||||
if window.get_window_type() != wnck.WINDOW_NORMAL:
|
||||
if window is None or window.get_window_type() != wnck.WINDOW_NORMAL:
|
||||
return
|
||||
|
||||
xid = window.get_xid()
|
||||
act = self._get_activity_by_xid(window.get_xid())
|
||||
if act:
|
||||
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
|
||||
act = self._get_activity_by_xid(xid)
|
||||
if act is None:
|
||||
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):
|
||||
self._activities.append(activity)
|
||||
self.emit('activity-added', activity)
|
||||
def _remove_activity(self, home_activity):
|
||||
if home_activity == self._active_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):
|
||||
if activity == self._current_activity:
|
||||
self._current_activity = None
|
||||
|
||||
self.emit('activity-removed', activity)
|
||||
self._activities.remove(activity)
|
||||
self.emit('activity-removed', home_activity)
|
||||
self._activities.remove(home_activity)
|
||||
|
||||
def _remove_activity_by_xid(self, xid):
|
||||
activity = self._get_activity_by_xid(xid)
|
||||
if activity:
|
||||
self._remove_activity(activity)
|
||||
home_activity = self._get_activity_by_xid(xid)
|
||||
if home_activity:
|
||||
self._remove_activity(home_activity)
|
||||
else:
|
||||
logging.error('Model for window %d does not exist.' % xid)
|
||||
|
||||
def notify_activity_launch(self, activity_id, service_name):
|
||||
bundle = self._bundle_registry.get_bundle(service_name)
|
||||
if not bundle:
|
||||
registry = activity.get_registry()
|
||||
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)
|
||||
activity = HomeActivity(bundle, activity_id)
|
||||
activity.props.launching = True
|
||||
self._add_activity(activity)
|
||||
home_activity = HomeActivity(activity_info, activity_id)
|
||||
home_activity.props.launching = True
|
||||
self._add_activity(home_activity)
|
||||
|
||||
def notify_activity_launch_failed(self, activity_id):
|
||||
activity = self._get_activity_by_id(activity_id)
|
||||
if activity:
|
||||
logging.debug("Activity %s (%s) launch failed" % (activity_id, activity.get_type()))
|
||||
self._remove_activity(activity)
|
||||
home_activity = self._get_activity_by_id(activity_id)
|
||||
if home_activity:
|
||||
logging.debug("Activity %s (%s) launch failed" % (activity_id, home_activity.get_type()))
|
||||
self._remove_activity(home_activity)
|
||||
else:
|
||||
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"""
|
||||
import dbus
|
||||
|
||||
from model import bundleregistry
|
||||
|
||||
_DBUS_SERVICE = "org.laptop.Shell"
|
||||
_DBUS_ACTIVITY_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry"
|
||||
_DBUS_SHELL_IFACE = "org.laptop.Shell"
|
||||
_DBUS_OWNER_IFACE = "org.laptop.Shell.Owner"
|
||||
_DBUS_PATH = "/org/laptop/Shell"
|
||||
@ -56,9 +53,6 @@ class ShellService(dbus.service.Object):
|
||||
self._home_model.connect('active-activity-changed',
|
||||
self._cur_activity_changed_cb)
|
||||
|
||||
bundle_registry = bundleregistry.get_registry()
|
||||
bundle_registry.connect('bundle-added', self._bundle_added_cb)
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
bus_name = dbus.service.BusName(_DBUS_SERVICE, bus=bus)
|
||||
dbus.service.Object.__init__(self, bus_name, _DBUS_PATH)
|
||||
@ -83,60 +77,6 @@ class ShellService(dbus.service.Object):
|
||||
def NotifyLaunchFailure(self, 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")
|
||||
def ColorChanged(self, color):
|
||||
pass
|
||||
@ -169,12 +109,3 @@ class ShellService(dbus.service.Object):
|
||||
if 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:
|
||||
menu_item = MenuItem(_('Make friend'), 'stock-add')
|
||||
menu_item.connect('activate', self._make_friend_cb)
|
||||
self.append_menu_item(menu_item)
|
||||
|
||||
self.menu.append(menu_item)
|
||||
menu_item.show()
|
||||
|
||||
activity = shell_model.get_home().get_current_activity()
|
||||
activity = self._shell.get_current_activity()
|
||||
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
|
||||
|
||||
menu_item = MenuItem(_('Invite'), 'stock-invite')
|
||||
menu_item.connect('activate', self._invite_friend_cb)
|
||||
self.append_menu_item(menu_item)
|
||||
self.menu.append(menu_item)
|
||||
menu_item.show()
|
||||
|
||||
def _buddy_icon_changed_cb(self, buddy):
|
||||
|
@ -26,6 +26,7 @@ import gtk
|
||||
import wnck
|
||||
|
||||
from sugar.activity.activityhandle import ActivityHandle
|
||||
from sugar import activity
|
||||
from sugar.activity import activityfactory
|
||||
from sugar.datastore import datastore
|
||||
from sugar import profile
|
||||
@ -34,7 +35,6 @@ from view.ActivityHost import ActivityHost
|
||||
from view.frame.frame import Frame
|
||||
from view.keyhandler import KeyHandler
|
||||
from view.home.HomeWindow import HomeWindow
|
||||
from model import bundleregistry
|
||||
from model.shellmodel import ShellModel
|
||||
from hardware import hardwaremanager
|
||||
|
||||
@ -47,6 +47,7 @@ class Shell(gobject.GObject):
|
||||
self._hosts = {}
|
||||
self._screen = wnck.screen_get_default()
|
||||
self._current_host = None
|
||||
self._pending_host = None
|
||||
self._screen_rotation = 0
|
||||
|
||||
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('active-activity-changed',
|
||||
self._active_activity_changed_cb)
|
||||
home_model.connect('pending-activity-changed',
|
||||
self._pending_activity_changed_cb)
|
||||
|
||||
# Unfreeze the display when it's stable
|
||||
hw_manager = hardwaremanager.get_manager()
|
||||
@ -100,6 +103,12 @@ class Shell(gobject.GObject):
|
||||
|
||||
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):
|
||||
return self._model
|
||||
|
||||
@ -107,16 +116,16 @@ class Shell(gobject.GObject):
|
||||
return self._frame
|
||||
|
||||
def join_activity(self, bundle_id, activity_id):
|
||||
activity = self.get_activity(activity_id)
|
||||
if activity:
|
||||
activity.present()
|
||||
activity_host = self.get_activity(activity_id)
|
||||
if activity_host:
|
||||
activity_host.present()
|
||||
return
|
||||
|
||||
# Get the service name for this activity, if
|
||||
# we have a bundle on the system capable of handling
|
||||
# this activity type
|
||||
breg = bundleregistry.get_registry()
|
||||
bundle = breg.get_bundle(bundle_id)
|
||||
registry = activity.get_registry()
|
||||
bundle = registry.get_activity(bundle_id)
|
||||
if not bundle:
|
||||
logging.error("Couldn't find activity for type %s" % bundle_id)
|
||||
return
|
||||
@ -156,6 +165,8 @@ class Shell(gobject.GObject):
|
||||
return
|
||||
|
||||
if level == ShellModel.ZOOM_ACTIVITY:
|
||||
if self._pending_host is not None:
|
||||
self._pending_host.present()
|
||||
self._screen.toggle_showing_desktop(False)
|
||||
else:
|
||||
self._model.set_zoom_level(level)
|
||||
|
@ -73,9 +73,9 @@ class ClipboardIcon(CanvasIcon):
|
||||
self._selected = selected
|
||||
if selected:
|
||||
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:
|
||||
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):
|
||||
cb_service = clipboardservice.get_instance()
|
||||
@ -107,11 +107,11 @@ class ClipboardIcon(CanvasIcon):
|
||||
def prelight(self, enter):
|
||||
if enter:
|
||||
self._hover = True
|
||||
self.props.background_color = color.BLACK.get_int()
|
||||
self.props.background_color = style.COLOR_BLACK.get_int()
|
||||
else:
|
||||
self._hover = False
|
||||
if self._selected:
|
||||
self.props.background_color = color.DESKTOP_BACKGROUND.get_int()
|
||||
self.props.background_color = style.COLOR_SELECTION_GREY.get_int()
|
||||
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.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.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')
|
||||
# TODO: Implement stopping downloads
|
||||
@ -77,7 +79,8 @@ class ClipboardMenu(Palette):
|
||||
|
||||
self._journal_item = MenuItem(_('Add to journal'), 'document-save')
|
||||
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)
|
||||
|
||||
@ -120,32 +123,8 @@ class ClipboardMenu(Palette):
|
||||
def _open_item_activate_cb(self, menu_item):
|
||||
if self._percent < 100:
|
||||
return
|
||||
|
||||
jobject = self._copy_to_journal()
|
||||
# TODO: we cannot simply call resume() right now because we would lock
|
||||
# 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)
|
||||
jobject.resume()
|
||||
|
||||
def _remove_item_activate_cb(self, menu_item):
|
||||
cb_service = clipboardservice.get_instance()
|
||||
|
@ -20,30 +20,33 @@ import logging
|
||||
from sugar.graphics.palette import Palette
|
||||
from sugar.graphics.xocolor import XoColor
|
||||
from sugar.graphics.iconbutton import IconButton
|
||||
from sugar.graphics import style
|
||||
from sugar import profile
|
||||
from sugar import activity
|
||||
|
||||
from model import bundleregistry
|
||||
from frameinvoker import FrameCanvasInvoker
|
||||
|
||||
class ActivityButton(IconButton):
|
||||
def __init__(self, activity):
|
||||
IconButton.__init__(self, icon_name=activity.get_icon())
|
||||
def __init__(self, activity_info):
|
||||
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.set_group_id('frame')
|
||||
self.set_palette(palette)
|
||||
|
||||
self._activity = activity
|
||||
self._activity_info = activity_info
|
||||
|
||||
def get_bundle_id(self):
|
||||
return self._activity.get_service_name()
|
||||
return self._activity_info.service_name
|
||||
|
||||
class InviteButton(IconButton):
|
||||
def __init__(self, activity, invite):
|
||||
IconButton.__init__(self, icon_name=activity.get_icon())
|
||||
def __init__(self, activity_model, invite):
|
||||
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
|
||||
|
||||
def get_activity_id(self):
|
||||
@ -64,12 +67,12 @@ class ActivitiesBox(hippo.CanvasBox):
|
||||
self._invite_to_item = {}
|
||||
self._invites = self._shell_model.get_invites()
|
||||
|
||||
bundle_registry = bundleregistry.get_registry()
|
||||
for bundle in bundle_registry:
|
||||
if bundle.get_show_launcher():
|
||||
self.add_activity(bundle)
|
||||
registry = activity.get_registry()
|
||||
for activity_info in registry.get_activities():
|
||||
if activity_info.show_launcher:
|
||||
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:
|
||||
self.add_invite(invite)
|
||||
@ -90,19 +93,19 @@ class ActivitiesBox(hippo.CanvasBox):
|
||||
def _invite_removed_cb(self, invites, invite):
|
||||
self.remove_invite(invite)
|
||||
|
||||
def _bundle_added_cb(self, bundle_registry, bundle):
|
||||
self.add_activity(bundle)
|
||||
def _activity_added_cb(self, activity_registry, activity_info):
|
||||
self.add_activity(activity_info)
|
||||
|
||||
def add_activity(self, activity):
|
||||
item = ActivityButton(activity)
|
||||
def add_activity(self, activity_info):
|
||||
item = ActivityButton(activity_info)
|
||||
item.connect('activated', self._activity_clicked_cb)
|
||||
self.append(item, 0)
|
||||
|
||||
def add_invite(self, invite):
|
||||
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:
|
||||
item = InviteButton(activity, invite)
|
||||
item = InviteButton(activity_model, invite)
|
||||
item.connect('activated', self._invite_clicked_cb)
|
||||
self.append(item, 0)
|
||||
|
||||
|
@ -20,8 +20,8 @@ import gobject
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from sugar.graphics import style
|
||||
from sugar.presence import presenceservice
|
||||
from sugar import activity
|
||||
|
||||
from model import bundleregistry
|
||||
from view.BuddyIcon import BuddyIcon
|
||||
|
||||
class FriendView(hippo.CanvasBox):
|
||||
@ -46,9 +46,9 @@ class FriendView(hippo.CanvasBox):
|
||||
self._buddy.connect('disappeared', self._buddy_disappeared_cb)
|
||||
self._buddy.connect('color-changed', self._buddy_color_changed_cb)
|
||||
|
||||
def _get_new_icon_name(self, activity):
|
||||
registry = bundleregistry.get_registry()
|
||||
bundle = registry.get_bundle(activity.get_type())
|
||||
def _get_new_icon_name(self, home_activity):
|
||||
registry = activity.get_registry()
|
||||
bundle = registry.get_bundle(home_activity.get_type())
|
||||
if bundle:
|
||||
return bundle.get_icon()
|
||||
return None
|
||||
@ -58,14 +58,14 @@ class FriendView(hippo.CanvasBox):
|
||||
self.remove(self._activity_icon)
|
||||
self._activity_icon_visible = False
|
||||
|
||||
def _buddy_activity_changed_cb(self, buddy, activity=None):
|
||||
if not activity:
|
||||
def _buddy_activity_changed_cb(self, buddy, home_activity=None):
|
||||
if not home_activity:
|
||||
self._remove_activity_icon()
|
||||
return
|
||||
|
||||
# FIXME: use some sort of "unknown activity" icon rather
|
||||
# than hiding the icon?
|
||||
name = self._get_new_icon_name(activity)
|
||||
name = self._get_new_icon_name(home_activity)
|
||||
if name:
|
||||
self._activity_icon.props.icon_name = name
|
||||
self._activity_icon.props.xo_color = buddy.get_color()
|
||||
@ -76,8 +76,8 @@ class FriendView(hippo.CanvasBox):
|
||||
self._remove_activity_icon()
|
||||
|
||||
def _buddy_appeared_cb(self, buddy):
|
||||
activity = self._buddy.get_current_activity()
|
||||
self._buddy_activity_changed_cb(buddy, activity)
|
||||
home_activity = self._buddy.get_current_activity()
|
||||
self._buddy_activity_changed_cb(buddy, home_activity)
|
||||
|
||||
def _buddy_disappeared_cb(self, buddy):
|
||||
self._buddy_activity_changed_cb(buddy, None)
|
||||
|
@ -130,7 +130,7 @@ class HomeMyIcon(MyIcon):
|
||||
|
||||
shutdown_menu_item = gtk.MenuItem(_('Shutdown'))
|
||||
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()
|
||||
|
||||
self.set_palette(palette)
|
||||
|
@ -88,14 +88,14 @@ class ActivityIcon(CanvasIcon):
|
||||
|
||||
resume_menu_item = gtk.MenuItem(_('Resume'))
|
||||
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()
|
||||
|
||||
# FIXME: kludge
|
||||
if self._activity.get_type() != "org.laptop.JournalActivity":
|
||||
stop_menu_item = gtk.MenuItem(_('Stop'))
|
||||
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()
|
||||
|
||||
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.connect('activity-added', self._activity_added_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)
|
||||
|
||||
@ -385,7 +385,7 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
||||
cr.fill()
|
||||
|
||||
# Selected Wedge
|
||||
current_activity = self._model.get_current_activity()
|
||||
current_activity = self._model.get_pending_activity()
|
||||
if current_activity is not None:
|
||||
selected_index = self._model.index(current_activity)
|
||||
[angle_start, angle_end] = self._get_angles(selected_index)
|
||||
|
@ -12,21 +12,19 @@ sugar_PYTHON = \
|
||||
util.py \
|
||||
wm.py
|
||||
|
||||
INCLUDES = \
|
||||
$(LIB_CFLAGS) \
|
||||
$(LIB_BINDINGS_CFLAGS) \
|
||||
$(PYTHON_INCLUDES) \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/lib/ui
|
||||
|
||||
pkgpyexecdir = $(pythondir)/sugar
|
||||
|
||||
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_LIBADD = \
|
||||
$(LIB_BINDINGS_LIBS) \
|
||||
$(LIB_LIBS) \
|
||||
$(top_builddir)/lib/libsugar.la
|
||||
|
||||
_sugarext_la_SOURCES = \
|
||||
@ -36,10 +34,16 @@ nodist__sugarext_la_SOURCES = _sugarext.c
|
||||
|
||||
_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_LIBADD = \
|
||||
$(LIB_BINDINGS_LIBS) \
|
||||
$(LIB_LIBS) \
|
||||
$(LIBUI_BINDINGS_LIBS) \
|
||||
$(LIBUI_LIBS) \
|
||||
$(top_builddir)/lib/ui/libsugarui.la
|
||||
|
||||
_sugaruiext_la_SOURCES = \
|
||||
|
@ -26,19 +26,27 @@
|
||||
|
||||
;; From sugar-menu.h
|
||||
|
||||
(define-method popup
|
||||
(define-method set_active
|
||||
(of-object "SugarMenu")
|
||||
(c-name "sugar_menu_popup")
|
||||
(c-name "sugar_menu_set_active")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("gint" "x")
|
||||
'("gint" "y")
|
||||
'("gboolean" "active")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method popdown
|
||||
(define-method embed
|
||||
(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")
|
||||
)
|
||||
|
||||
|
@ -18,6 +18,7 @@ modulename _sugarext
|
||||
import gobject.GObject as PyGObject_Type
|
||||
import gtk.Entry as PyGtkEntry_Type
|
||||
import gtk.Menu as PyGtkMenu_Type
|
||||
import gtk.Container as PyGtkContainer_Type
|
||||
import gtk.gdk.Window as PyGdkWindow_Type
|
||||
%%
|
||||
ignore-glob
|
||||
|
@ -114,7 +114,7 @@ class ActivityCreationHandler(gobject.GObject):
|
||||
|
||||
self._factory.create(self._activity_handle.get_dict(),
|
||||
timeout=120 * 1000,
|
||||
reply_handler=self._no_reply_handler,
|
||||
reply_handler=self._create_reply_handler,
|
||||
error_handler=self._create_error_handler)
|
||||
|
||||
def get_activity_id(self):
|
||||
@ -137,7 +137,10 @@ class ActivityCreationHandler(gobject.GObject):
|
||||
def _activate_error_handler(self, 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)." %
|
||||
(self._activity_handle.activity_id, self._service_name))
|
||||
|
||||
|
@ -41,6 +41,7 @@ class NotInstalledException(Exception): pass
|
||||
class InvalidPathException(Exception): pass
|
||||
class ZipExtractException(Exception): pass
|
||||
class RegistrationException(Exception): pass
|
||||
class MalformedBundleException(Exception): pass
|
||||
|
||||
class Bundle:
|
||||
"""Metadata description of a given application/activity
|
||||
@ -265,10 +266,12 @@ class Bundle:
|
||||
if not bundle_root_dir:
|
||||
bundle_root_dir = file_name.split('/')[0]
|
||||
if not bundle_root_dir.endswith('.activity'):
|
||||
raise 'Incorrect bundle.'
|
||||
raise MalformedBundleException(
|
||||
'The activity directory name must end with .activity')
|
||||
else:
|
||||
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
|
||||
|
||||
@ -294,10 +297,7 @@ class Bundle:
|
||||
|
||||
self._init_with_path(bundle_path)
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
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):
|
||||
if not activity.get_registry().add_bundle(bundle_path):
|
||||
raise RegistrationException
|
||||
|
||||
def deinstall(self):
|
||||
|
@ -162,8 +162,7 @@ def _get_mo_list(manifest):
|
||||
|
||||
for lang in _get_po_list(manifest).keys():
|
||||
filename = _get_service_name() + '.mo'
|
||||
mo_list.append(os.path.join(_get_source_path(), 'locale',
|
||||
lang, 'LC_MESSAGES', filename))
|
||||
mo_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename))
|
||||
|
||||
return mo_list
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
# Copyright (C) 2007 One Laptop Per Child
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
@ -18,29 +19,39 @@
|
||||
import logging
|
||||
|
||||
import dbus
|
||||
import gobject
|
||||
|
||||
_SHELL_SERVICE = "org.laptop.Shell"
|
||||
_SHELL_PATH = "/org/laptop/Shell"
|
||||
_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry"
|
||||
_ACTIVITY_REGISTRY_SERVICE_NAME = 'org.laptop.ActivityRegistry'
|
||||
_ACTIVITY_REGISTRY_IFACE = 'org.laptop.ActivityRegistry'
|
||||
_ACTIVITY_REGISTRY_PATH = '/org/laptop/ActivityRegistry'
|
||||
|
||||
def _activity_info_from_dict(info_dict):
|
||||
if not info_dict:
|
||||
return None
|
||||
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):
|
||||
def __init__(self, name, icon, service_name, path):
|
||||
def __init__(self, name, icon, service_name, path, show_launcher):
|
||||
self.name = name
|
||||
self.icon = icon
|
||||
self.service_name = service_name
|
||||
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):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
bus_object = bus.get_object(_SHELL_SERVICE, _SHELL_PATH)
|
||||
self._registry = dbus.Interface(bus_object, _REGISTRY_IFACE)
|
||||
bus_object = bus.get_object(_ACTIVITY_REGISTRY_SERVICE_NAME,
|
||||
_ACTIVITY_REGISTRY_PATH)
|
||||
self._registry = dbus.Interface(bus_object, _ACTIVITY_REGISTRY_IFACE)
|
||||
self._registry.connect_to_signal('ActivityAdded', self._activity_added_cb)
|
||||
|
||||
# Two caches fo saving some travel across dbus.
|
||||
@ -55,6 +66,10 @@ class ActivityRegistry(object):
|
||||
|
||||
return result
|
||||
|
||||
def get_activities(self):
|
||||
info_list = self._registry.GetActivities()
|
||||
return self._convert_info_list(info_list)
|
||||
|
||||
def get_activity(self, service_name):
|
||||
if self._service_name_to_activity_info.has_key(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
|
||||
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')
|
||||
self._service_name_to_activity_info.clear()
|
||||
self._mime_type_to_activities.clear()
|
||||
self.emit('activity-added', _activity_info_from_dict(info_dict))
|
||||
|
||||
_registry = None
|
||||
|
||||
|
@ -231,13 +231,13 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
stroke_color = None
|
||||
if self._active:
|
||||
if self._fill_color:
|
||||
fill_color = self._fill_color.get_html()
|
||||
fill_color = self._fill_color.get_svg()
|
||||
if self._stroke_color:
|
||||
stroke_color = self._stroke_color.get_html()
|
||||
stroke_color = self._stroke_color.get_svg()
|
||||
else:
|
||||
stroke_color = color.ICON_STROKE_INACTIVE.get_html()
|
||||
stroke_color = color.ICON_STROKE_INACTIVE.get_svg()
|
||||
if self._fill_color:
|
||||
fill_color = self._fill_color.get_html()
|
||||
fill_color = self._fill_color.get_svg()
|
||||
return [fill_color, stroke_color]
|
||||
|
||||
def _get_handle(self, name, handle):
|
||||
|
@ -53,7 +53,7 @@ class IconButton(CanvasIcon, hippo.CanvasItem):
|
||||
if self.props.active:
|
||||
self.props.background_color = 0x000000FF
|
||||
else:
|
||||
self.props.background_color = 0x404040FF
|
||||
self.props.background_color = 0x00000000
|
||||
|
||||
def _icon_clicked_cb(self, button):
|
||||
if self._palette:
|
||||
|
@ -21,12 +21,12 @@ import time
|
||||
import gtk
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.frame import Frame
|
||||
from sugar.activity.bundle import Bundle
|
||||
from sugar.date import Date
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from sugar.graphics.xocolor import XoColor
|
||||
from sugar.graphics.canvasroundbox import CanvasRoundBox
|
||||
from sugar.datastore import datastore
|
||||
from sugar import activity
|
||||
from sugar.objects import objecttype
|
||||
@ -95,12 +95,12 @@ class ObjectChooser(gtk.Dialog):
|
||||
else:
|
||||
return None
|
||||
|
||||
class CollapsedEntry(Frame):
|
||||
class CollapsedEntry(CanvasRoundBox):
|
||||
_DATE_COL_WIDTH = style.zoom(100)
|
||||
_BUDDIES_COL_WIDTH = style.zoom(50)
|
||||
|
||||
def __init__(self, jobject):
|
||||
Frame.__init__(self)
|
||||
CanvasRoundBox.__init__(self)
|
||||
self.props.box_height = style.zoom(75)
|
||||
self.props.spacing = style.DEFAULT_SPACING
|
||||
self.props.border_color = style.COLOR_BLACK.get_int()
|
||||
|
@ -36,7 +36,40 @@ _RIGHT_TOP = 5
|
||||
_TOP_LEFT = 6
|
||||
_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
|
||||
AT_CURSOR = 1
|
||||
AROUND = 2
|
||||
@ -54,7 +87,9 @@ class Palette(gobject.GObject):
|
||||
'invoker' : (object, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'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__ = {
|
||||
@ -65,16 +100,21 @@ class Palette(gobject.GObject):
|
||||
}
|
||||
|
||||
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._cursor_x = 0
|
||||
self._cursor_y = 0
|
||||
self._state = self._SECONDARY
|
||||
self._state = self._PRIMARY
|
||||
self._invoker = None
|
||||
self._group_id = None
|
||||
self._up = False
|
||||
self._position = self.DEFAULT
|
||||
self._draw_gap = False
|
||||
self._palette_popup_sid = None
|
||||
|
||||
self._popup_anim = animator.Animator(0.3, 10)
|
||||
@ -86,60 +126,70 @@ class Palette(gobject.GObject):
|
||||
self._popdown_anim = animator.Animator(0.6, 10)
|
||||
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._menu.append(self._primary)
|
||||
self._primary.show()
|
||||
self._label = gtk.Label()
|
||||
vbox.pack_start(self._label, False)
|
||||
|
||||
self._separator = gtk.SeparatorMenuItem()
|
||||
self._menu.append(self._separator)
|
||||
self._secondary_box = gtk.VBox()
|
||||
vbox.pack_start(self._secondary_box)
|
||||
|
||||
self._content = _ContentMenuItem()
|
||||
self._menu.append(self._content)
|
||||
self._separator = gtk.HSeparator()
|
||||
self._secondary_box.pack_start(self._separator)
|
||||
|
||||
self._button_bar = _ButtonBarMenuItem()
|
||||
self._menu.append(self._button_bar)
|
||||
self._menu_box = gtk.VBox()
|
||||
self._secondary_box.pack_start(self._menu_box)
|
||||
self._menu_box.show()
|
||||
|
||||
self._menu.connect('enter-notify-event',
|
||||
self._content = gtk.VBox()
|
||||
self._secondary_box.pack_start(self._content)
|
||||
self._content.show()
|
||||
|
||||
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._menu.connect('leave-notify-event',
|
||||
self.connect('leave-notify-event',
|
||||
self._leave_notify_event_cb)
|
||||
|
||||
self.set_primary_text(label, accel_path)
|
||||
|
||||
def is_up(self):
|
||||
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):
|
||||
self._primary.set_label(label, accel_path)
|
||||
|
||||
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
|
||||
self._label.set_text(label)
|
||||
self._label.show()
|
||||
|
||||
def set_content(self, widget):
|
||||
self._content.set_widget(widget)
|
||||
self._content.show()
|
||||
if len(self._content.get_children()) > 0:
|
||||
self.remove(self._content.get_children()[0])
|
||||
|
||||
def append_button(self, button):
|
||||
self._button_bar.append_button(button)
|
||||
self._button_bar.show()
|
||||
if widget is not None:
|
||||
self._content.add(widget)
|
||||
|
||||
self._update_accept_focus()
|
||||
self._update_separator()
|
||||
|
||||
def set_group_id(self, group_id):
|
||||
if self._group_id:
|
||||
@ -154,9 +204,11 @@ class Palette(gobject.GObject):
|
||||
self._invoker = value
|
||||
self._invoker.connect('mouse-enter', self._invoker_mouse_enter_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':
|
||||
self._position = value
|
||||
elif pspec.name == 'draw-gap':
|
||||
self._draw_gap = value
|
||||
self.queue_draw()
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
@ -165,9 +217,57 @@ class Palette(gobject.GObject):
|
||||
return self._invoker
|
||||
elif pspec.name == 'position':
|
||||
return self._position
|
||||
elif pspec.name == 'draw-gap':
|
||||
return self._draw_gap
|
||||
else:
|
||||
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):
|
||||
[width, height] = self._full_request
|
||||
screen_area = self._invoker.get_screen_area()
|
||||
@ -182,7 +282,7 @@ class Palette(gobject.GObject):
|
||||
if inv_rect == None:
|
||||
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 + \
|
||||
palette_width * palette_halign
|
||||
@ -241,12 +341,12 @@ class Palette(gobject.GObject):
|
||||
def _update_full_request(self):
|
||||
state = self._state
|
||||
|
||||
self._menu.set_size_request(-1, -1)
|
||||
self.set_size_request(-1, -1)
|
||||
|
||||
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)
|
||||
|
||||
@ -282,7 +382,7 @@ class Palette(gobject.GObject):
|
||||
elif position == self.TOP:
|
||||
x, y = self._get_top_position()
|
||||
|
||||
self._menu.popup(x, y)
|
||||
self.move(x, y)
|
||||
|
||||
def _show(self):
|
||||
if self._up:
|
||||
@ -291,11 +391,12 @@ class Palette(gobject.GObject):
|
||||
self._update_cursor_position()
|
||||
self._update_full_request()
|
||||
|
||||
self._invoker.connect_to_parent()
|
||||
self._palette_popup_sid = _palette_observer.connect('popup',
|
||||
self._palette_observer_popup_cb)
|
||||
self._palette_popup_sid = _palette_observer.connect(
|
||||
'popup', self._palette_observer_popup_cb)
|
||||
|
||||
self._update_position()
|
||||
self.menu.set_active(True)
|
||||
self.show()
|
||||
|
||||
self._up = True
|
||||
_palette_observer.emit('popup', self)
|
||||
@ -305,7 +406,8 @@ class Palette(gobject.GObject):
|
||||
if not self._palette_popup_sid is None:
|
||||
_palette_observer.disconnect(self._palette_popup_sid)
|
||||
self._palette_popup_sid = None
|
||||
self._menu.popdown()
|
||||
self.menu.set_active(False)
|
||||
self.hide()
|
||||
|
||||
self._up = False
|
||||
self.emit('popdown')
|
||||
@ -329,25 +431,11 @@ class Palette(gobject.GObject):
|
||||
return
|
||||
|
||||
if state == self._PRIMARY:
|
||||
self._primary.show()
|
||||
for menu_item in self._menu.get_children()[1:]:
|
||||
menu_item.hide()
|
||||
self.menu.unembed()
|
||||
self._secondary_box.hide()
|
||||
elif state == self._SECONDARY:
|
||||
middle_menu_items = self._menu.get_children()
|
||||
middle_menu_items = middle_menu_items[2:len(middle_menu_items) - 2]
|
||||
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.menu.embed(self._menu_box)
|
||||
self._secondary_box.show()
|
||||
|
||||
self._state = state
|
||||
|
||||
@ -357,68 +445,54 @@ class Palette(gobject.GObject):
|
||||
def _invoker_mouse_leave_cb(self, invoker):
|
||||
self.popdown()
|
||||
|
||||
def _invoker_focus_out_cb(self, invoker):
|
||||
self._hide()
|
||||
|
||||
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._secondary_anim.start()
|
||||
|
||||
def _leave_notify_event_cb(self, widget, event):
|
||||
if event.detail == gtk.gdk.NOTIFY_NONLINEAR:
|
||||
if event.detail != gtk.gdk.NOTIFY_INFERIOR:
|
||||
self.popdown()
|
||||
|
||||
def _palette_observer_popup_cb(self, observer, palette):
|
||||
if self != palette:
|
||||
self._hide()
|
||||
|
||||
class _PrimaryMenuItem(gtk.MenuItem):
|
||||
def __init__(self, label, accel_path):
|
||||
gtk.MenuItem.__init__(self)
|
||||
self.set_border_width(style.DEFAULT_PADDING)
|
||||
self._set_label(label, accel_path)
|
||||
class PaletteActionBar(gtk.HButtonBox):
|
||||
def add_action(label, icon_name=None):
|
||||
button = Button(label)
|
||||
|
||||
def set_label(self, label, accel_path):
|
||||
self.remove(self._label)
|
||||
self._set_label(label, accel_path)
|
||||
if icon_name:
|
||||
icon = Icon(icon_name)
|
||||
button.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
def _set_label(self, label, accel_path):
|
||||
self._label = gtk.AccelLabel(label)
|
||||
self._label.set_accel_widget(self)
|
||||
self.pack_start(button)
|
||||
button.show()
|
||||
|
||||
if accel_path:
|
||||
self.set_accel_path(accel_path)
|
||||
self._label.set_alignment(0.0, 0.5)
|
||||
class _Menu(_sugaruiext.Menu):
|
||||
__gtype_name__ = 'SugarPaletteMenu'
|
||||
|
||||
self.add(self._label)
|
||||
self._label.show()
|
||||
def __init__(self, palette):
|
||||
_sugaruiext.Menu.__init__(self)
|
||||
self._palette = palette
|
||||
|
||||
class _ContentMenuItem(gtk.MenuItem):
|
||||
def __init__(self):
|
||||
gtk.MenuItem.__init__(self)
|
||||
def do_insert(self, item, position):
|
||||
_sugaruiext.Menu.do_insert(self, item, position)
|
||||
self._palette._update_separator()
|
||||
|
||||
def set_widget(self, widget):
|
||||
if self.child:
|
||||
self.remove(self.child)
|
||||
self.add(widget)
|
||||
def do_expose_event(self, event):
|
||||
# Ignore the Menu expose, just do the MenuShell expose to prevent any
|
||||
# border from being drawn here. A border is drawn by the palette object
|
||||
# around everything.
|
||||
gtk.MenuShell.do_expose_event(self, event)
|
||||
|
||||
def is_empty(self):
|
||||
return self.child is None or not self.child.props.visible
|
||||
def do_grab_notify(self, was_grabbed):
|
||||
# Ignore grab_notify as the menu would close otherwise
|
||||
pass
|
||||
|
||||
class _ButtonBarMenuItem(gtk.MenuItem):
|
||||
def __init__(self):
|
||||
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
|
||||
def do_deactivate(self):
|
||||
self._palette._hide()
|
||||
|
||||
class _PopupAnimation(animator.Animation):
|
||||
def __init__(self, palette):
|
||||
@ -469,13 +543,6 @@ class Invoker(gobject.GObject):
|
||||
height = gtk.gdk.screen_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):
|
||||
def __init__(self, widget):
|
||||
Invoker.__init__(self)
|
||||
@ -495,6 +562,37 @@ class WidgetInvoker(Invoker):
|
||||
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
|
||||
def draw_invoker_rect(self, event, palette):
|
||||
style = self._widget.style
|
||||
if palette.is_up():
|
||||
gap = _calculate_gap(self.get_rect(), palette.get_rect())
|
||||
|
||||
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):
|
||||
self.emit('mouse-enter')
|
||||
|
||||
|
@ -22,6 +22,8 @@ from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, WidgetInvoker
|
||||
|
||||
class RadioToolButton(gtk.RadioToolButton):
|
||||
__gtype_name__ = "SugarRadioToolButton"
|
||||
|
||||
def __init__(self, named_icon=None, group=None):
|
||||
gtk.RadioToolButton.__init__(self, group=group)
|
||||
self._palette = None
|
||||
@ -38,9 +40,25 @@ class RadioToolButton(gtk.RadioToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self._palette = Palette(text)
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
|
||||
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)
|
||||
|
@ -81,6 +81,12 @@ class Color(object):
|
||||
|
||||
return (r, g, b)
|
||||
|
||||
def get_svg(self):
|
||||
if self._a == 0.0:
|
||||
return 'none'
|
||||
else:
|
||||
return self.get_html()
|
||||
|
||||
_XO_DPI = 200.0
|
||||
|
||||
_FOCUS_LINE_WIDTH = 2
|
||||
@ -113,6 +119,7 @@ TOOLBOX_TAB_LABEL_WIDTH = zoom(150 - 15 * 2)
|
||||
|
||||
COLOR_BLACK = Color('#000000')
|
||||
COLOR_WHITE = Color('#FFFFFF')
|
||||
COLOR_TRANSPARENT = Color('#FFFFFF', alpha=0.0)
|
||||
COLOR_PANEL_GREY = Color('#C0C0C0')
|
||||
COLOR_SELECTION_GREY = Color('#A6A6A6')
|
||||
COLOR_INACTIVE_FILL = Color('#9D9FA1')
|
||||
|
@ -21,6 +21,8 @@ from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, WidgetInvoker
|
||||
|
||||
class ToggleToolButton(gtk.ToggleToolButton):
|
||||
__gtype_name__ = "SugarToggleToolButton"
|
||||
|
||||
def __init__(self, named_icon=None):
|
||||
gtk.ToggleToolButton.__init__(self)
|
||||
self._palette = None
|
||||
@ -37,9 +39,25 @@ class ToggleToolButton(gtk.ToggleToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self._palette = Palette(text)
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
|
||||
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)
|
||||
|
@ -23,6 +23,7 @@ from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, WidgetInvoker
|
||||
|
||||
class ToolButton(gtk.ToolButton):
|
||||
__gtype_name__ = "SugarToolButton"
|
||||
|
||||
def __init__(self, icon_name=None):
|
||||
gtk.ToolButton.__init__(self)
|
||||
@ -41,12 +42,28 @@ class ToolButton(gtk.ToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.props.draw_gap:
|
||||
if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_invoker_rect(event, self._palette)
|
||||
|
||||
gtk.ToolButton.do_expose_event(self, event)
|
||||
|
||||
def _button_clicked_cb(self, widget):
|
||||
if self._palette:
|
||||
self._palette.popdown(True)
|
||||
|
||||
def _palette_changed(self, palette):
|
||||
# Force a redraw to update the invoker rectangle
|
||||
self.queue_draw()
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
Loading…
Reference in New Issue
Block a user