Big refactor of the directory structure and packages to
reflect private/public
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
sugardir = $(pythondir)/sugar/shell
|
||||
sugar_PYTHON = \
|
||||
__init__.py \
|
||||
activity.py \
|
||||
shell.py \
|
||||
Owner.py \
|
||||
PresenceWindow.py \
|
||||
StartPage.py
|
||||
@@ -0,0 +1,57 @@
|
||||
import os
|
||||
import random
|
||||
import base64
|
||||
|
||||
from sugar import env
|
||||
from sugar.presence import Service
|
||||
from sugar.presence import Buddy
|
||||
from sugar.presence import PresenceService
|
||||
from sugar.p2p import Stream
|
||||
|
||||
class ShellOwner(object):
|
||||
"""Class representing the owner of this machine/instance. This class
|
||||
runs in the shell and serves up the buddy icon and other stuff. It's the
|
||||
server portion of the Owner, paired with the client portion in Buddy.py."""
|
||||
def __init__(self):
|
||||
nick = env.get_nick_name()
|
||||
user_dir = env.get_user_dir()
|
||||
if not os.path.exists(user_dir):
|
||||
try:
|
||||
os.makedirs(user_dir)
|
||||
except OSError:
|
||||
print "Could not create user directory."
|
||||
|
||||
self._icon = None
|
||||
for fname in os.listdir(user_dir):
|
||||
if not fname.startswith("buddy-icon."):
|
||||
continue
|
||||
fd = open(os.path.join(user_dir, fname), "r")
|
||||
self._icon = fd.read()
|
||||
fd.close()
|
||||
break
|
||||
|
||||
# Our presence service
|
||||
port = random.randint(40000, 65000)
|
||||
properties = {}
|
||||
self._service = Service.Service(nick, Buddy.PRESENCE_SERVICE_TYPE,
|
||||
domain="", address=None, port=port, properties=properties)
|
||||
print "Owner '%s' using port %d" % (nick, port)
|
||||
|
||||
self._icon_stream = Stream.Stream.new_from_service(self._service)
|
||||
self._icon_stream.register_reader_handler(self._handle_buddy_icon_request, "get_buddy_icon")
|
||||
|
||||
# Announce ourselves to the world
|
||||
self._pservice = PresenceService.PresenceService.get_instance()
|
||||
self._pservice.start()
|
||||
self._pservice.register_service(self._service)
|
||||
|
||||
def _handle_buddy_icon_request(self):
|
||||
"""XMLRPC method, return the owner's icon encoded with base64."""
|
||||
if self._icon:
|
||||
return base64.b64encode(self._icon)
|
||||
return ""
|
||||
|
||||
def set_icon(self, icon):
|
||||
"""Can only set icon in constructor for now."""
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
from sugar.presence.PresenceService import PresenceService
|
||||
|
||||
class PresenceWindow(gtk.Window):
|
||||
_MODEL_COL_NICK = 0
|
||||
_MODEL_COL_ICON = 1
|
||||
_MODEL_COL_BUDDY = 2
|
||||
_MODEL_COL_VISIBLE = 3
|
||||
|
||||
def __init__(self, activity_container):
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
self._activity_container = activity_container
|
||||
self._activity = None
|
||||
|
||||
self._pservice = PresenceService.get_instance()
|
||||
self._pservice.connect("buddy-appeared", self._on_buddy_appeared_cb)
|
||||
self._pservice.connect("buddy-disappeared", self._on_buddy_disappeared_cb)
|
||||
self._pservice.set_debug(True)
|
||||
self._pservice.start()
|
||||
|
||||
self._setup_ui()
|
||||
|
||||
def _is_buddy_visible(self, buddy):
|
||||
if self._activity:
|
||||
activity_type = self._activity.get_default_type()
|
||||
service = buddy.get_service_of_type(activity_type, self._activity)
|
||||
return service is not None
|
||||
else:
|
||||
return True
|
||||
|
||||
def _update_buddies_visibility(self):
|
||||
for row in self._buddy_store:
|
||||
row[self._MODEL_COL_VISIBLE] = self._is_buddy_visible(row[self._MODEL_COL_BUDDY])
|
||||
|
||||
def set_activity(self, activity):
|
||||
self._activity = activity
|
||||
self._update_buddies_visibility()
|
||||
|
||||
def _setup_ui(self):
|
||||
vbox = gtk.VBox(False, 6)
|
||||
vbox.set_border_width(12)
|
||||
|
||||
label = gtk.Label("Who's around:")
|
||||
label.set_alignment(0.0, 0.5)
|
||||
vbox.pack_start(label, False)
|
||||
label.show()
|
||||
|
||||
self._buddy_store = gtk.ListStore(gobject.TYPE_STRING,
|
||||
gtk.gdk.Pixbuf,
|
||||
gobject.TYPE_PYOBJECT,
|
||||
bool)
|
||||
buddy_list_model = self._buddy_store.filter_new()
|
||||
buddy_list_model.set_visible_column(self._MODEL_COL_VISIBLE)
|
||||
|
||||
sw = gtk.ScrolledWindow()
|
||||
sw.set_shadow_type(gtk.SHADOW_IN)
|
||||
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
|
||||
self._buddy_list_view = gtk.TreeView(buddy_list_model)
|
||||
self._buddy_list_view.set_headers_visible(False)
|
||||
self._buddy_list_view.connect("cursor-changed", self._on_buddyList_buddy_selected)
|
||||
self._buddy_list_view.connect("row-activated", self._on_buddyList_buddy_double_clicked)
|
||||
|
||||
sw.add(self._buddy_list_view)
|
||||
self._buddy_list_view.show()
|
||||
|
||||
renderer = gtk.CellRendererPixbuf()
|
||||
column = gtk.TreeViewColumn("", renderer, pixbuf=self._MODEL_COL_ICON)
|
||||
column.set_resizable(False)
|
||||
column.set_expand(False);
|
||||
self._buddy_list_view.append_column(column)
|
||||
|
||||
renderer = gtk.CellRendererText()
|
||||
column = gtk.TreeViewColumn("", renderer, text=self._MODEL_COL_NICK)
|
||||
column.set_resizable(True)
|
||||
column.set_sizing("GTK_TREE_VIEW_COLUMN_GROW_ONLY");
|
||||
column.set_expand(True);
|
||||
self._buddy_list_view.append_column(column)
|
||||
|
||||
vbox.pack_start(sw)
|
||||
sw.show()
|
||||
|
||||
button_box = gtk.HButtonBox()
|
||||
|
||||
share_button = gtk.Button('Share')
|
||||
share_button.connect('clicked', self._share_button_clicked_cb)
|
||||
button_box.pack_start(share_button)
|
||||
share_button.show()
|
||||
|
||||
vbox.pack_start(button_box, False)
|
||||
button_box.show()
|
||||
|
||||
self.add(vbox)
|
||||
vbox.show()
|
||||
|
||||
def _share_button_clicked_cb(self, button):
|
||||
self._activity_container.current_activity.publish()
|
||||
|
||||
def _on_buddyList_buddy_selected(self, view, *args):
|
||||
(model, aniter) = view.get_selection().get_selected()
|
||||
name = model.get(aniter, self._MODEL_COL_NICK)
|
||||
|
||||
def _on_buddyList_buddy_double_clicked(self, view, *args):
|
||||
""" Select the chat for this buddy or group """
|
||||
(model, aniter) = view.get_selection().get_selected()
|
||||
chat = None
|
||||
buddy = view.get_model().get_value(aniter, self._MODEL_COL_BUDDY)
|
||||
if buddy and not self._chats.has_key(buddy):
|
||||
#chat = BuddyChat(self, buddy)
|
||||
#self._chats[buddy] = chat
|
||||
#chat.connect_to_shell()
|
||||
pass
|
||||
|
||||
def __buddy_icon_changed_cb(self, buddy):
|
||||
it = self._get_iter_for_buddy(buddy)
|
||||
self._buddy_store.set(it, self._MODEL_COL_ICON, buddy.get_icon_pixbuf())
|
||||
|
||||
def _on_buddy_appeared_cb(self, pservice, buddy):
|
||||
if buddy.is_owner():
|
||||
# Do not show ourself in the buddy list
|
||||
return
|
||||
|
||||
aniter = self._buddy_store.append(None)
|
||||
self._buddy_store.set(aniter,
|
||||
self._MODEL_COL_NICK, buddy.get_nick_name(),
|
||||
self._MODEL_COL_BUDDY, buddy,
|
||||
self._MODEL_COL_VISIBLE, self._is_buddy_visible(buddy))
|
||||
buddy.connect('icon-changed', self.__buddy_icon_changed_cb)
|
||||
buddy.connect('service-added', self.__buddy_service_added_cb)
|
||||
buddy.connect('service-removed', self.__buddy_service_removed_cb)
|
||||
|
||||
def __buddy_service_added_cb(self, buddy, service):
|
||||
self._update_buddies_visibility()
|
||||
|
||||
def __buddy_service_removed_cb(self, buddy, service):
|
||||
self._update_buddies_visibility()
|
||||
|
||||
def _on_buddy_disappeared_cb(self, pservice, buddy):
|
||||
aniter = self._get_iter_for_buddy(buddy)
|
||||
if aniter:
|
||||
self._buddy_store.remove(aniter)
|
||||
|
||||
def _get_iter_for_buddy(self, buddy):
|
||||
aniter = self._buddy_store.get_iter_first()
|
||||
while aniter:
|
||||
list_buddy = self._buddy_store.get_value(aniter, self._MODEL_COL_BUDDY)
|
||||
if buddy == list_buddy:
|
||||
return aniter
|
||||
aniter = self._buddy_store.iter_next(aniter)
|
||||
@@ -0,0 +1,295 @@
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import pango
|
||||
import dbus
|
||||
import cgi
|
||||
import xml.sax.saxutils
|
||||
import gobject
|
||||
|
||||
from google import google
|
||||
from sugar.presence.PresenceService import PresenceService
|
||||
|
||||
_BROWSER_ACTIVITY_TYPE = "_web_olpc._udp"
|
||||
|
||||
_COLUMN_TITLE = 0
|
||||
_COLUMN_ADDRESS = 1
|
||||
_COLUMN_SUBTITLE = 2
|
||||
_COLUMN_SERVICE = 3
|
||||
|
||||
class SearchHelper(object):
|
||||
def __init__(self, activity_uid):
|
||||
self.search_uid = activity_uid
|
||||
self.found = False
|
||||
|
||||
class SearchModel(gtk.ListStore):
|
||||
def __init__(self, activities_model, search_text):
|
||||
gtk.ListStore.__init__(self, gobject.TYPE_STRING, gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
|
||||
|
||||
for row in activities_model:
|
||||
title = row[_COLUMN_TITLE]
|
||||
address = row[_COLUMN_ADDRESS]
|
||||
if title.find(search_text) >= 0 or address.find(search_text) >= 0:
|
||||
self.append([ title, address, row[_COLUMN_SUBTITLE], row[_COLUMN_SERVICE] ])
|
||||
|
||||
google.LICENSE_KEY = '1As9KaJQFHIJ1L0W5EZPl6vBOFvh/Vaf'
|
||||
data = google.doGoogleSearch(search_text)
|
||||
|
||||
for result in data.results:
|
||||
title = result.title
|
||||
|
||||
# FIXME what tags should we actually strip?
|
||||
title = title.replace('<b>', '')
|
||||
title = title.replace('</b>', '')
|
||||
|
||||
# FIXME I'm sure there is a better way to
|
||||
# unescape these.
|
||||
title = title.replace('"', '"')
|
||||
title = title.replace('&', '&')
|
||||
|
||||
self.append([ title, result.URL, None, None ])
|
||||
|
||||
class ActivitiesModel(gtk.ListStore):
|
||||
def __init__(self):
|
||||
gtk.ListStore.__init__(self, gobject.TYPE_STRING, gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
|
||||
|
||||
def _filter_dupe_activities(self, model, path, it, user_data):
|
||||
"""Search the list of list rows for an existing service that
|
||||
has the activity ID we're looking for."""
|
||||
helper = user_data
|
||||
(service, ) = model.get(it, _COLUMN_SERVICE)
|
||||
if not service:
|
||||
return False
|
||||
if service.get_activity_uid() == helper.search_uid:
|
||||
helper.found = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_activity(self, buddy, service):
|
||||
# Web Activity check
|
||||
activity_uid = service.get_activity_uid()
|
||||
if activity_uid is None:
|
||||
return
|
||||
# Don't show dupes
|
||||
helper = SearchHelper(activity_uid)
|
||||
self.foreach(self._filter_dupe_activities, helper)
|
||||
if helper.found == True:
|
||||
return
|
||||
|
||||
# Only accept browser activities for now
|
||||
if service.get_type() == _BROWSER_ACTIVITY_TYPE:
|
||||
escaped_title = service.get_one_property('Title')
|
||||
escaped_uri = service.get_one_property('URI')
|
||||
if escaped_title and escaped_uri:
|
||||
title = xml.sax.saxutils.unescape(escaped_title)
|
||||
address = xml.sax.saxutils.unescape(escaped_uri)
|
||||
subtitle = 'Shared by %s' % buddy.get_nick_name()
|
||||
self.append([ title, address, subtitle, service ])
|
||||
|
||||
class ActivitiesView(gtk.TreeView):
|
||||
def __init__(self, model):
|
||||
gtk.TreeView.__init__(self, model)
|
||||
|
||||
self._owner = None
|
||||
|
||||
self.set_headers_visible(False)
|
||||
|
||||
theme = gtk.icon_theme_get_default()
|
||||
size = 48
|
||||
self._web_pixbuf = theme.load_icon('emblem-web', size, 0)
|
||||
self._share_pixbuf = theme.load_icon('emblem-people', size, 0)
|
||||
|
||||
column = gtk.TreeViewColumn('')
|
||||
self.append_column(column)
|
||||
|
||||
cell = gtk.CellRendererPixbuf()
|
||||
column.pack_start(cell, False)
|
||||
column.set_cell_data_func(cell, self._icon_cell_data_func)
|
||||
|
||||
cell = gtk.CellRendererText()
|
||||
column.pack_start(cell)
|
||||
column.set_cell_data_func(cell, self._cell_data_func)
|
||||
|
||||
self.connect('row-activated', self._row_activated_cb)
|
||||
|
||||
def _icon_cell_data_func(self, column, cell, model, it):
|
||||
if model.get_value(it, _COLUMN_SERVICE) == None:
|
||||
cell.set_property('pixbuf', self._web_pixbuf)
|
||||
else:
|
||||
cell.set_property('pixbuf', self._share_pixbuf)
|
||||
|
||||
def _cell_data_func(self, column, cell, model, it):
|
||||
title = model.get_value(it, _COLUMN_TITLE)
|
||||
subtitle = model.get_value(it, _COLUMN_SUBTITLE)
|
||||
if subtitle is None:
|
||||
subtitle = model.get_value(it, _COLUMN_ADDRESS)
|
||||
|
||||
markup = '<big><b>' + cgi.escape(title) + '</b></big>'
|
||||
markup += '\n' + cgi.escape(subtitle)
|
||||
|
||||
cell.set_property('markup', markup)
|
||||
cell.set_property('ellipsize', pango.ELLIPSIZE_END)
|
||||
|
||||
def set_owner(self, owner):
|
||||
self._owner = owner
|
||||
|
||||
def _row_activated_cb(self, treeview, path, column):
|
||||
bus = dbus.SessionBus()
|
||||
proxy_obj = bus.get_object('com.redhat.Sugar.Browser', '/com/redhat/Sugar/Browser')
|
||||
browser_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.BrowserShell')
|
||||
|
||||
model = self.get_model()
|
||||
address = model.get_value(model.get_iter(path), _COLUMN_ADDRESS)
|
||||
service = model.get_value(model.get_iter(path), _COLUMN_SERVICE)
|
||||
|
||||
print 'Activated row %s' % address
|
||||
|
||||
if service is None:
|
||||
browser_shell.open_browser(address)
|
||||
else:
|
||||
if not self._owner:
|
||||
raise RuntimeError("We don't have an owner yet!")
|
||||
serialized_service = service.serialize(self._owner)
|
||||
browser_shell.open_browser(address, serialized_service)
|
||||
|
||||
class StartPage(gtk.HBox):
|
||||
def __init__(self, ac_signal_object):
|
||||
gtk.HBox.__init__(self)
|
||||
|
||||
self._ac_signal_object = ac_signal_object
|
||||
self._ac_signal_object.connect("local-activity-started",
|
||||
self._on_local_activity_started_cb)
|
||||
self._ac_signal_object.connect("local-activity-ended",
|
||||
self._on_local_activity_ended_cb)
|
||||
|
||||
self._pservice = PresenceService.get_instance()
|
||||
self._pservice.connect("activity-announced", self._on_activity_announced_cb)
|
||||
self._pservice.connect("new-service-adv", self._on_new_service_adv_cb)
|
||||
self._pservice.connect("buddy-appeared", self._on_buddy_appeared_cb)
|
||||
self._pservice.connect("buddy-disappeared", self._on_buddy_disappeared_cb)
|
||||
self._pservice.start()
|
||||
self._pservice.track_service_type(_BROWSER_ACTIVITY_TYPE)
|
||||
if self._pservice.get_owner():
|
||||
self._on_buddy_appeared_cb(self._pservice, self._pservice.get_owner())
|
||||
|
||||
vbox = gtk.VBox()
|
||||
|
||||
search_box = gtk.HBox(False, 6)
|
||||
search_box.set_border_width(24)
|
||||
|
||||
self._search_entry = gtk.Entry()
|
||||
self._search_entry.connect('activate', self._search_entry_activate_cb)
|
||||
search_box.pack_start(self._search_entry)
|
||||
self._search_entry.show()
|
||||
|
||||
search_button = gtk.Button("Search")
|
||||
search_button.connect('clicked', self._search_button_clicked_cb)
|
||||
search_box.pack_start(search_button, False)
|
||||
search_button.show()
|
||||
|
||||
vbox.pack_start(search_box, False, True)
|
||||
search_box.show()
|
||||
|
||||
exp_space = gtk.Label('')
|
||||
vbox.pack_start(exp_space)
|
||||
exp_space.show()
|
||||
|
||||
self.pack_start(vbox)
|
||||
vbox.show()
|
||||
|
||||
vbox = gtk.VBox()
|
||||
|
||||
self._search_close_box = gtk.HBox()
|
||||
|
||||
self._search_close_label = gtk.Label()
|
||||
self._search_close_label.set_alignment(0.0, 0.5)
|
||||
self._search_close_box.pack_start(self._search_close_label)
|
||||
self._search_close_label.show()
|
||||
|
||||
close_image = gtk.Image()
|
||||
close_image.set_from_stock (gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
|
||||
close_image.show()
|
||||
|
||||
search_close_button = gtk.Button()
|
||||
rcstyle = gtk.RcStyle();
|
||||
rcstyle.xthickness = rcstyle.ythickness = 0;
|
||||
search_close_button.modify_style (rcstyle);
|
||||
search_close_button.add(close_image)
|
||||
search_close_button.set_relief(gtk.RELIEF_NONE)
|
||||
search_close_button.set_focus_on_click(False)
|
||||
search_close_button.connect("clicked", self.__search_close_button_clicked_cb)
|
||||
|
||||
self._search_close_box.pack_start(search_close_button, False)
|
||||
search_close_button.show()
|
||||
|
||||
vbox.pack_start(self._search_close_box, False)
|
||||
|
||||
sw = gtk.ScrolledWindow()
|
||||
sw.set_size_request(320, -1)
|
||||
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
|
||||
self._activities_model = ActivitiesModel()
|
||||
|
||||
owner = self._pservice.get_owner()
|
||||
self._activities = ActivitiesView(self._activities_model)
|
||||
sw.add(self._activities)
|
||||
self._activities.show()
|
||||
|
||||
vbox.pack_start(sw)
|
||||
sw.show()
|
||||
|
||||
self.pack_start(vbox)
|
||||
vbox.show()
|
||||
|
||||
def __search_close_button_clicked_cb(self, button):
|
||||
self._search(None)
|
||||
|
||||
def _on_local_activity_started_cb(self, helper, activity_container, activity_id):
|
||||
print "new local activity %s" % activity_id
|
||||
|
||||
def _on_local_activity_ended_cb(self, helper, activity_container, activity_id):
|
||||
print "local activity %s disappeared" % activity_id
|
||||
|
||||
def _on_new_service_adv_cb(self, pservice, activity_id, short_stype):
|
||||
if activity_id:
|
||||
self._pservice.track_service_type(short_stype)
|
||||
|
||||
def _on_buddy_appeared_cb(self, pservice, buddy):
|
||||
if buddy.is_owner():
|
||||
self._activities.set_owner(buddy)
|
||||
|
||||
def _on_buddy_disappeared_cb(self, pservice, buddy):
|
||||
if buddy.is_owner():
|
||||
self._activities.set_owner(None)
|
||||
|
||||
def _on_activity_announced_cb(self, pservice, service, buddy):
|
||||
print "Found new activity with type %s" % service.get_full_type()
|
||||
self._activities_model.add_activity(buddy, service)
|
||||
if self._activities.get_model() != self._activities_model:
|
||||
self._search(self._last_search)
|
||||
|
||||
def _search_entry_activate_cb(self, entry):
|
||||
self._search()
|
||||
self._search_entry.set_text('')
|
||||
|
||||
def _search_button_clicked_cb(self, button):
|
||||
self._search()
|
||||
self._search_entry.set_text('')
|
||||
|
||||
def _search(self, text = None):
|
||||
if text == None:
|
||||
text = self._search_entry.get_text()
|
||||
|
||||
if text == None or len(text) == 0:
|
||||
self._activities.set_model(self._activities_model)
|
||||
self._search_close_box.hide()
|
||||
else:
|
||||
search_model = SearchModel(self._activities_model, text)
|
||||
self._activities.set_model(search_model)
|
||||
|
||||
self._search_close_label.set_text('Search for %s' % (text))
|
||||
self._search_close_box.show()
|
||||
|
||||
self._last_search = text
|
||||
@@ -0,0 +1,158 @@
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
SM_SPACE_PROPORTIONAL = 0
|
||||
SM_STEP = 1
|
||||
|
||||
SLIDING_TIMEOUT = 50
|
||||
SLIDING_MODE = SM_SPACE_PROPORTIONAL
|
||||
|
||||
#SLIDING_TIMEOUT = 10
|
||||
#SLIDING_MODE = SM_STEP
|
||||
#SLIDING_STEP = 0.05
|
||||
|
||||
class WindowManager:
|
||||
__managers_list = []
|
||||
|
||||
CENTER = 0
|
||||
LEFT = 1
|
||||
RIGHT = 2
|
||||
TOP = 3
|
||||
BOTTOM = 4
|
||||
|
||||
ABSOLUTE = 0
|
||||
SCREEN_RELATIVE = 1
|
||||
|
||||
def __init__(self, window):
|
||||
self._window = window
|
||||
self._sliding_pos = 0
|
||||
|
||||
WindowManager.__managers_list.append(self)
|
||||
|
||||
window.connect("key-press-event", self.__key_press_event_cb)
|
||||
|
||||
def __key_press_event_cb(self, window, event):
|
||||
manager = None
|
||||
|
||||
if event.keyval == gtk.keysyms.Left and \
|
||||
event.state & gtk.gdk.CONTROL_MASK:
|
||||
for wm in WindowManager.__managers_list:
|
||||
if wm._position == WindowManager.LEFT:
|
||||
manager = wm
|
||||
|
||||
if event.keyval == gtk.keysyms.Up and \
|
||||
event.state & gtk.gdk.CONTROL_MASK:
|
||||
for wm in WindowManager.__managers_list:
|
||||
if wm._position == WindowManager.TOP:
|
||||
manager = wm
|
||||
|
||||
if manager and manager._window.get_property('visible'):
|
||||
manager.slide_window_out()
|
||||
elif manager:
|
||||
manager.slide_window_in()
|
||||
|
||||
def set_width(self, width, width_type):
|
||||
self._width = width
|
||||
self._width_type = width_type
|
||||
|
||||
def set_height(self, height, height_type):
|
||||
self._height = height
|
||||
self._height_type = height_type
|
||||
|
||||
def set_position(self, position):
|
||||
self._position = position
|
||||
|
||||
def _calc_size_and_position(self):
|
||||
screen_width = self._window.get_screen().get_width()
|
||||
screen_height = self._window.get_screen().get_height()
|
||||
|
||||
if self._width_type is WindowManager.ABSOLUTE:
|
||||
width = self._width
|
||||
elif self._width_type is WindowManager.SCREEN_RELATIVE:
|
||||
width = int(screen_width * self._width)
|
||||
|
||||
if self._height_type is WindowManager.ABSOLUTE:
|
||||
height = self._height
|
||||
elif self._height_type is WindowManager.SCREEN_RELATIVE:
|
||||
height = int(screen_height * self._height)
|
||||
|
||||
if self._position is WindowManager.CENTER:
|
||||
self._x = int((screen_width - width) / 2)
|
||||
self._y = int((screen_height - height) / 2)
|
||||
elif self._position is WindowManager.LEFT:
|
||||
self._x = - int((1.0 - self._sliding_pos) * width)
|
||||
self._y = int((screen_height - height) / 2)
|
||||
elif self._position is WindowManager.TOP:
|
||||
self._x = int((screen_width - width) / 2)
|
||||
self._y = - int((1.0 - self._sliding_pos) * height)
|
||||
|
||||
self._real_width = width
|
||||
self._real_height = height
|
||||
|
||||
def _update_size_and_position(self):
|
||||
self._calc_size_and_position()
|
||||
self._window.move(self._x, self._y)
|
||||
self._window.resize(self._real_width, self._real_height)
|
||||
|
||||
def _update_position(self):
|
||||
self._calc_size_and_position()
|
||||
self._window.move(self._x, self._y)
|
||||
|
||||
def __slide_in_timeout_cb(self):
|
||||
if self._sliding_pos == 0:
|
||||
self._window.show()
|
||||
|
||||
if SLIDING_MODE == SM_SPACE_PROPORTIONAL:
|
||||
space_to_go = 1.0 - self._sliding_pos
|
||||
self._sliding_pos += (space_to_go / 2)
|
||||
else:
|
||||
self._sliding_pos += SLIDING_STEP
|
||||
|
||||
if self._sliding_pos > .999:
|
||||
self._sliding_pos = 1.0
|
||||
|
||||
self._update_position()
|
||||
|
||||
if self._sliding_pos == 1.0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def __slide_out_timeout_cb(self):
|
||||
self._window.show()
|
||||
|
||||
if SLIDING_MODE == SM_SPACE_PROPORTIONAL:
|
||||
space_to_go = self._sliding_pos
|
||||
self._sliding_pos -= (space_to_go / 2)
|
||||
else:
|
||||
self._sliding_pos -= SLIDING_STEP
|
||||
|
||||
if self._sliding_pos < .001:
|
||||
self._sliding_pos = 0
|
||||
|
||||
self._update_position()
|
||||
|
||||
if self._sliding_pos == 0:
|
||||
self._window.hide()
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def slide_window_in(self):
|
||||
self._sliding_pos = 0
|
||||
gobject.timeout_add(SLIDING_TIMEOUT, self.__slide_in_timeout_cb)
|
||||
|
||||
def slide_window_out(self):
|
||||
self._sliding_pos = 1.0
|
||||
gobject.timeout_add(SLIDING_TIMEOUT, self.__slide_out_timeout_cb)
|
||||
|
||||
def show(self):
|
||||
self._window.show()
|
||||
|
||||
def update(self):
|
||||
self._update_size_and_position()
|
||||
|
||||
def manage(self):
|
||||
self._update_size_and_position()
|
||||
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Facade that hides the differences between the SOAPpy and SOAP.py
|
||||
libraries, so that google.py doesn't have to deal with them.
|
||||
|
||||
@author: Brian Landers <brian@bluecoat93.org>
|
||||
@license: Python
|
||||
@version: 0.5.4
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
__author__ = "Brian Landers <brian@bluecoat93.org>"
|
||||
__version__ = "0.6"
|
||||
__license__ = "Python"
|
||||
|
||||
#
|
||||
# Wrapper around the python 'warnings' facility
|
||||
#
|
||||
def warn( message, level=RuntimeWarning ):
|
||||
warnings.warn( message, level, stacklevel=3 )
|
||||
|
||||
# We can't use older version of SOAPpy, due to bugs that break the Google API
|
||||
minSOAPpyVersion = "0.11.3"
|
||||
|
||||
#
|
||||
# Try loading SOAPpy first. If that fails, fall back to the old SOAP.py
|
||||
#
|
||||
SOAPpy = None
|
||||
try:
|
||||
import SOAPpy
|
||||
from SOAPpy import SOAPProxy, Types
|
||||
|
||||
if LooseVersion( minSOAPpyVersion ) > \
|
||||
LooseVersion( SOAPpy.version.__version__ ):
|
||||
|
||||
warn( "Versions of SOAPpy before %s have known bugs that prevent " +
|
||||
"PyGoogle from functioning." % minSOAPpyVersion )
|
||||
raise ImportError
|
||||
|
||||
except ImportError:
|
||||
warn( "SOAPpy not imported. Trying legacy SOAP.py.",
|
||||
DeprecationWarning )
|
||||
try:
|
||||
import SOAP
|
||||
except ImportError:
|
||||
raise RuntimeError( "Unable to find SOAPpy or SOAP. Can't continue.\n" )
|
||||
|
||||
#
|
||||
# Constants that differ between the modules
|
||||
#
|
||||
if SOAPpy:
|
||||
false = Types.booleanType(0)
|
||||
true = Types.booleanType(1)
|
||||
structType = Types.structType
|
||||
faultType = Types.faultType
|
||||
else:
|
||||
false = SOAP.booleanType(0)
|
||||
true = SOAP.booleanType(1)
|
||||
structType = SOAP.structType
|
||||
faultType = SOAP.faultType
|
||||
|
||||
#
|
||||
# Get a SOAP Proxy object in the correct way for the module we're using
|
||||
#
|
||||
def getProxy( url, namespace, http_proxy ):
|
||||
if SOAPpy:
|
||||
return SOAPProxy( url,
|
||||
namespace = namespace,
|
||||
http_proxy = http_proxy )
|
||||
|
||||
else:
|
||||
return SOAP.SOAPProxy( url,
|
||||
namespace = namespace,
|
||||
http_proxy = http_proxy )
|
||||
|
||||
#
|
||||
# Convert an object to a dictionary in the proper way for the module
|
||||
# we're using for SOAP
|
||||
#
|
||||
def toDict( obj ):
|
||||
if SOAPpy:
|
||||
return obj._asdict()
|
||||
else:
|
||||
return obj._asdict
|
||||
@@ -0,0 +1,5 @@
|
||||
cut_n_pastedir = $(datadir)/sugar
|
||||
cut_n_paste_PYTHON = \
|
||||
google.py \
|
||||
GoogleSOAPFacade.py \
|
||||
SOAP.py
|
||||
Executable
+3974
File diff suppressed because it is too large
Load Diff
Executable
+638
@@ -0,0 +1,638 @@
|
||||
"""
|
||||
Python wrapper for Google web APIs
|
||||
|
||||
This module allows you to access Google's web APIs through SOAP,
|
||||
to do things like search Google and get the results programmatically.
|
||||
Described U{here <http://www.google.com/apis/>}
|
||||
|
||||
You need a Google-provided license key to use these services.
|
||||
Follow the link above to get one. These functions will look in
|
||||
several places (in this order) for the license key:
|
||||
|
||||
- the "license_key" argument of each function
|
||||
- the module-level LICENSE_KEY variable (call setLicense once to set it)
|
||||
- an environment variable called GOOGLE_LICENSE_KEY
|
||||
- a file called ".googlekey" in the current directory
|
||||
- a file called "googlekey.txt" in the current directory
|
||||
- a file called ".googlekey" in your home directory
|
||||
- a file called "googlekey.txt" in your home directory
|
||||
- a file called ".googlekey" in the same directory as google.py
|
||||
- a file called "googlekey.txt" in the same directory as google.py
|
||||
|
||||
Sample usage::
|
||||
|
||||
>>> import google
|
||||
>>> google.setLicense('...') # must get your own key!
|
||||
>>> data = google.doGoogleSearch('python')
|
||||
>>> data.meta.searchTime
|
||||
0.043221000000000002
|
||||
|
||||
>>> data.results[0].URL
|
||||
'http://www.python.org/'
|
||||
|
||||
>>> data.results[0].title
|
||||
'<b>Python</b> Language Website'
|
||||
|
||||
@newfield contrib: Contributors
|
||||
@author: Mark Pilgrim <f8dy@diveintomark.org>
|
||||
@author: Brian Landers <brian@bluecoat93.org>
|
||||
@license: Python
|
||||
@version: 0.6
|
||||
@contrib: David Ascher, for the install script
|
||||
@contrib: Erik Max Francis, for the command line interface
|
||||
@contrib: Michael Twomey, for HTTP proxy support
|
||||
@contrib: Mark Recht, for patches to support SOAPpy
|
||||
"""
|
||||
|
||||
__author__ = "Mark Pilgrim (f8dy@diveintomark.org)"
|
||||
__version__ = "0.6"
|
||||
__cvsversion__ = "$Revision: 1.5 $"[11:-2]
|
||||
__date__ = "$Date: 2004/02/25 23:46:07 $"[7:-2]
|
||||
__copyright__ = "Copyright (c) 2002 Mark Pilgrim"
|
||||
__license__ = "Python"
|
||||
__credits__ = """David Ascher, for the install script
|
||||
Erik Max Francis, for the command line interface
|
||||
Michael Twomey, for HTTP proxy support"""
|
||||
|
||||
import os, sys, getopt
|
||||
import GoogleSOAPFacade
|
||||
|
||||
LICENSE_KEY = None
|
||||
HTTP_PROXY = None
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
_url = 'http://api.google.com/search/beta2'
|
||||
_namespace = 'urn:GoogleSearch'
|
||||
_googlefile1 = ".googlekey"
|
||||
_googlefile2 = "googlekey.txt"
|
||||
|
||||
_false = GoogleSOAPFacade.false
|
||||
_true = GoogleSOAPFacade.true
|
||||
|
||||
_licenseLocations = (
|
||||
( lambda key: key,
|
||||
'passed to the function in license_key variable' ),
|
||||
( lambda key: LICENSE_KEY,
|
||||
'module-level LICENSE_KEY variable (call setLicense to set it)' ),
|
||||
( lambda key: os.environ.get( 'GOOGLE_LICENSE_KEY', None ),
|
||||
'an environment variable called GOOGLE_LICENSE_KEY' ),
|
||||
( lambda key: _contentsOf( os.getcwd(), _googlefile1 ),
|
||||
'%s in the current directory' % _googlefile1),
|
||||
( lambda key: _contentsOf( os.getcwd(), _googlefile2 ),
|
||||
'%s in the current directory' % _googlefile2),
|
||||
( lambda key: _contentsOf( os.environ.get( 'HOME', '' ), _googlefile1 ),
|
||||
'%s in your home directory' % _googlefile1),
|
||||
( lambda key: _contentsOf( os.environ.get( 'HOME', '' ), _googlefile2 ),
|
||||
'%s in your home directory' % _googlefile2 ),
|
||||
( lambda key: _contentsOf( _getScriptDir(), _googlefile1 ),
|
||||
'%s in the google.py directory' % _googlefile1 ),
|
||||
( lambda key: _contentsOf( _getScriptDir(), _googlefile2 ),
|
||||
'%s in the google.py directory' % _googlefile2 )
|
||||
)
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## Exceptions
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class NoLicenseKey(Exception):
|
||||
"""
|
||||
Thrown when the API is unable to find a valid license key.
|
||||
"""
|
||||
pass
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## administrative functions (non-API)
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def _version():
|
||||
"""
|
||||
Display a formatted version string for the module
|
||||
"""
|
||||
print """PyGoogle %(__version__)s
|
||||
%(__copyright__)s
|
||||
released %(__date__)s
|
||||
|
||||
Thanks to:
|
||||
%(__credits__)s""" % globals()
|
||||
|
||||
|
||||
def _usage():
|
||||
"""
|
||||
Display usage information for the command-line interface
|
||||
"""
|
||||
program = os.path.basename(sys.argv[0])
|
||||
print """Usage: %(program)s [options] [querytype] query
|
||||
|
||||
options:
|
||||
-k, --key= <license key> Google license key (see important note below)
|
||||
-1, -l, --lucky show only first hit
|
||||
-m, --meta show meta information
|
||||
-r, --reverse show results in reverse order
|
||||
-x, --proxy= <url> use HTTP proxy
|
||||
-h, --help print this help
|
||||
-v, --version print version and copyright information
|
||||
-t, --test run test queries
|
||||
|
||||
querytype:
|
||||
-s, --search= <query> search (default)
|
||||
-c, --cache= <url> retrieve cached page
|
||||
-p, --spelling= <word> check spelling
|
||||
|
||||
IMPORTANT NOTE: all Google functions require a valid license key;
|
||||
visit http://www.google.com/apis/ to get one. %(program)s will look in
|
||||
these places (in order) and use the first license key it finds:
|
||||
* the key specified on the command line""" % vars()
|
||||
for get, location in _licenseLocations[2:]:
|
||||
print " *", location
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## utility functions (API)
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def setLicense(license_key):
|
||||
"""
|
||||
Set the U{Google APIs <http://www.google.com/api>} license key
|
||||
|
||||
@param license_key: The new key to use
|
||||
@type license_key: String
|
||||
@todo: validate the key?
|
||||
"""
|
||||
global LICENSE_KEY
|
||||
LICENSE_KEY = license_key
|
||||
|
||||
|
||||
def getLicense(license_key = None):
|
||||
"""
|
||||
Get the U{Google APIs <http://www.google.com/api>} license key
|
||||
|
||||
The key can be read from any number of locations. See the module-leve
|
||||
documentation for the search order.
|
||||
|
||||
@return: the license key
|
||||
@rtype: String
|
||||
@raise NoLicenseKey: if no valid key could be found
|
||||
"""
|
||||
for get, location in _licenseLocations:
|
||||
rc = get(license_key)
|
||||
if rc: return rc
|
||||
_usage()
|
||||
raise NoLicenseKey, 'get a license key at http://www.google.com/apis/'
|
||||
|
||||
|
||||
def setProxy(http_proxy):
|
||||
"""
|
||||
Set the HTTP proxy to be used when accessing Google
|
||||
|
||||
@param http_proxy: the proxy to use
|
||||
@type http_proxy: String
|
||||
@todo: validiate the input?
|
||||
"""
|
||||
global HTTP_PROXY
|
||||
HTTP_PROXY = http_proxy
|
||||
|
||||
|
||||
def getProxy(http_proxy = None):
|
||||
"""
|
||||
Get the HTTP proxy we use for accessing Google
|
||||
|
||||
@return: the proxy
|
||||
@rtype: String
|
||||
"""
|
||||
return http_proxy or HTTP_PROXY
|
||||
|
||||
|
||||
def _contentsOf(dirname, filename):
|
||||
filename = os.path.join(dirname, filename)
|
||||
if not os.path.exists(filename): return None
|
||||
fsock = open(filename)
|
||||
contents = fsock.read()
|
||||
fsock.close()
|
||||
return contents
|
||||
|
||||
|
||||
def _getScriptDir():
|
||||
if __name__ == '__main__':
|
||||
return os.path.abspath(os.path.dirname(sys.argv[0]))
|
||||
else:
|
||||
return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__))
|
||||
|
||||
|
||||
def _marshalBoolean(value):
|
||||
if value:
|
||||
return _true
|
||||
else:
|
||||
return _false
|
||||
|
||||
|
||||
def _getRemoteServer( http_proxy ):
|
||||
return GoogleSOAPFacade.getProxy( _url, _namespace, http_proxy )
|
||||
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## search results classes
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class _SearchBase:
|
||||
def __init__(self, params):
|
||||
for k, v in params.items():
|
||||
if isinstance(v, GoogleSOAPFacade.structType):
|
||||
v = GoogleSOAPFacade.toDict( v )
|
||||
|
||||
try:
|
||||
if isinstance(v[0], GoogleSOAPFacade.structType):
|
||||
v = [ SOAPProxy.toDict( node ) for node in v ]
|
||||
|
||||
except:
|
||||
pass
|
||||
self.__dict__[str(k)] = v
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class SearchResultsMetaData(_SearchBase):
|
||||
"""
|
||||
Container class for metadata about a given search query's results.
|
||||
|
||||
@ivar documentFiltering: is duplicate page filtering active?
|
||||
|
||||
@ivar searchComments: human-readable informational message
|
||||
|
||||
example::
|
||||
|
||||
"'the' is a very common word and was not included in your search"
|
||||
|
||||
@ivar estimatedTotalResultsCount: estimated total number of results
|
||||
for this query.
|
||||
|
||||
@ivar estimateIsExact: is estimatedTotalResultsCount an exact value?
|
||||
|
||||
@ivar searchQuery: search string that initiated this search
|
||||
|
||||
@ivar startIndex: index of the first result returned (zero-based)
|
||||
|
||||
@ivar endIndex: index of the last result returned (zero-based)
|
||||
|
||||
@ivar searchTips: human-readable informational message on how to better
|
||||
use Google.
|
||||
|
||||
@ivar directoryCategories: list of categories for the search results
|
||||
|
||||
This field is a list of dictionaries, like so::
|
||||
|
||||
{ 'fullViewableName': 'the Open Directory category',
|
||||
'specialEncoding': 'encoding scheme of this directory category'
|
||||
}
|
||||
|
||||
@ivar searchTime: total search time, in seconds
|
||||
"""
|
||||
pass
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class SearchResult(_SearchBase):
|
||||
"""
|
||||
Encapsulates the results from a search.
|
||||
|
||||
@ivar URL: URL
|
||||
|
||||
@ivar title: title (HTML)
|
||||
|
||||
@ivar snippet: snippet showing query context (HTML
|
||||
|
||||
@ivar cachedSize: size of cached version of this result, (KB)
|
||||
|
||||
@ivar relatedInformationPresent: is the "related:" keyword supported?
|
||||
|
||||
Flag indicates that the "related:" keyword is supported for this URL
|
||||
|
||||
@ivar hostName: used when filtering occurs
|
||||
|
||||
When filtering occurs, a maximum of two results from any given
|
||||
host is returned. When this occurs, the second resultElement
|
||||
that comes from that host contains the host name in this parameter.
|
||||
|
||||
@ivar directoryCategory: Open Directory category information
|
||||
|
||||
This field is a dictionary with the following values::
|
||||
|
||||
{ 'fullViewableName': 'the Open Directory category',
|
||||
'specialEncoding' : 'encoding scheme of this directory category'
|
||||
}
|
||||
|
||||
@ivar directoryTitle: Open Directory title of this result (or blank)
|
||||
|
||||
@ivar summary: Open Directory summary for this result (or blank)
|
||||
"""
|
||||
pass
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class SearchReturnValue:
|
||||
"""
|
||||
complete search results for a single query
|
||||
|
||||
@ivar meta: L{SearchResultsMetaData} instance for this query
|
||||
|
||||
@ivar results: list of L{SearchResult} objects for this query
|
||||
"""
|
||||
def __init__( self, metadata, results ):
|
||||
self.meta = metadata
|
||||
self.results = results
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## main functions
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def doGoogleSearch( q, start = 0, maxResults = 10, filter = 1,
|
||||
restrict='', safeSearch = 0, language = '',
|
||||
inputencoding = '', outputencoding = '',\
|
||||
license_key = None, http_proxy = None ):
|
||||
"""
|
||||
Search Google using the SOAP API and return the results.
|
||||
|
||||
You need a license key to call this function; see the
|
||||
U{Google APIs <http://www.google.com/apis/>} site to get one.
|
||||
Then you can either pass it to this function every time, or
|
||||
set it globally; see the L{google} module-level docs for details.
|
||||
|
||||
See U{http://www.google.com/help/features.html}
|
||||
for examples of advanced features. Anything that works at the
|
||||
Google web site will work as a query string in this method.
|
||||
|
||||
You can use the C{start} and C{maxResults} parameters to page
|
||||
through multiple pages of results. Note that 'maxResults' is
|
||||
currently limited by Google to 10.
|
||||
|
||||
See the API reference for more advanced examples and a full list of
|
||||
country codes and topics for use in the C{restrict} parameter, along
|
||||
with legal values for the C{language}, C{inputencoding}, and
|
||||
C{outputencoding} parameters.
|
||||
|
||||
You can download the API documentation
|
||||
U{http://www.google.com/apis/download.html <here>}.
|
||||
|
||||
@param q: search string.
|
||||
@type q: String
|
||||
|
||||
@param start: (optional) zero-based index of first desired result.
|
||||
@type start: int
|
||||
|
||||
@param maxResults: (optional) maximum number of results to return.
|
||||
@type maxResults: int
|
||||
|
||||
@param filter: (optional) flag to request filtering of similar results
|
||||
@type filter: int
|
||||
|
||||
@param restrict: (optional) restrict results by country or topic.
|
||||
@type restrict: String
|
||||
|
||||
@param safeSearch: (optional)
|
||||
@type safeSearch: int
|
||||
|
||||
@param language: (optional)
|
||||
@type language: String
|
||||
|
||||
@param inputencoding: (optional)
|
||||
@type inputencoding: String
|
||||
|
||||
@param outputencoding: (optional)
|
||||
@type outputencoding: String
|
||||
|
||||
@param license_key: (optional) the Google API license key to use
|
||||
@type license_key: String
|
||||
|
||||
@param http_proxy: (optional) the HTTP proxy to use for talking to Google
|
||||
@type http_proxy: String
|
||||
|
||||
@return: the search results encapsulated in an object
|
||||
@rtype: L{SearchReturnValue}
|
||||
"""
|
||||
license_key = getLicense( license_key )
|
||||
http_proxy = getProxy( http_proxy )
|
||||
remoteserver = _getRemoteServer( http_proxy )
|
||||
|
||||
filter = _marshalBoolean( filter )
|
||||
safeSearch = _marshalBoolean( safeSearch )
|
||||
|
||||
data = remoteserver.doGoogleSearch( license_key, q, start, maxResults,
|
||||
filter, restrict, safeSearch,
|
||||
language, inputencoding,
|
||||
outputencoding )
|
||||
|
||||
metadata = GoogleSOAPFacade.toDict( data )
|
||||
del metadata["resultElements"]
|
||||
|
||||
metadata = SearchResultsMetaData( metadata )
|
||||
|
||||
results = [ SearchResult( GoogleSOAPFacade.toDict( node ) ) \
|
||||
for node in data.resultElements ]
|
||||
|
||||
return SearchReturnValue( metadata, results )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def doGetCachedPage( url, license_key = None, http_proxy = None ):
|
||||
"""
|
||||
Retrieve a page from the Google cache.
|
||||
|
||||
You need a license key to call this function; see the
|
||||
U{Google APIs <http://www.google.com/apis/>} site to get one.
|
||||
Then you can either pass it to this function every time, or
|
||||
set it globally; see the L{google} module-level docs for details.
|
||||
|
||||
@param url: full URL to the page to retrieve
|
||||
@type url: String
|
||||
|
||||
@param license_key: (optional) the Google API key to use
|
||||
@type license_key: String
|
||||
|
||||
@param http_proxy: (optional) the HTTP proxy server to use
|
||||
@type http_proxy: String
|
||||
|
||||
@return: full text of the cached page
|
||||
@rtype: String
|
||||
"""
|
||||
license_key = getLicense( license_key )
|
||||
http_proxy = getProxy( http_proxy )
|
||||
remoteserver = _getRemoteServer( http_proxy )
|
||||
|
||||
return remoteserver.doGetCachedPage( license_key, url )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def doSpellingSuggestion( phrase, license_key = None, http_proxy = None ):
|
||||
"""
|
||||
Get spelling suggestions from Google
|
||||
|
||||
You need a license key to call this function; see the
|
||||
U{Google APIs <http://www.google.com/apis/>} site to get one.
|
||||
Then you can either pass it to this function every time, or
|
||||
set it globally; see the L{google} module-level docs for details.
|
||||
|
||||
@param phrase: word or phrase to spell-check
|
||||
@type phrase: String
|
||||
|
||||
@param license_key: (optional) the Google API key to use
|
||||
@type license_key: String
|
||||
|
||||
@param http_proxy: (optional) the HTTP proxy to use
|
||||
@type http_proxy: String
|
||||
|
||||
@return: text of any suggested replacement, or None
|
||||
"""
|
||||
license_key = getLicense( license_key )
|
||||
http_proxy = getProxy( http_proxy)
|
||||
remoteserver = _getRemoteServer( http_proxy )
|
||||
|
||||
return remoteserver.doSpellingSuggestion( license_key, phrase )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## functional test suite (see googletest.py for unit test suite)
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def _test():
|
||||
"""
|
||||
Run functional test suite.
|
||||
"""
|
||||
try:
|
||||
getLicense(None)
|
||||
except NoLicenseKey:
|
||||
return
|
||||
|
||||
print "Searching for Python at google.com..."
|
||||
data = doGoogleSearch( "Python" )
|
||||
_output( data, { "func": "doGoogleSearch"} )
|
||||
|
||||
print "\nSearching for 5 _French_ pages about Python, "
|
||||
print "encoded in ISO-8859-1..."
|
||||
|
||||
data = doGoogleSearch( "Python", language = 'lang_fr',
|
||||
outputencoding = 'ISO-8859-1',
|
||||
maxResults = 5 )
|
||||
|
||||
_output( data, { "func": "doGoogleSearch" } )
|
||||
|
||||
phrase = "Pyhton programming languager"
|
||||
print "\nTesting spelling suggestions for '%s'..." % phrase
|
||||
|
||||
data = doSpellingSuggestion( phrase )
|
||||
|
||||
_output( data, { "func": "doSpellingSuggestion" } )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## Command-line interface
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class _OutputFormatter:
|
||||
def boil(self, data):
|
||||
if type(data) == type(u""):
|
||||
return data.encode("ISO-8859-1", "replace")
|
||||
else:
|
||||
return data
|
||||
|
||||
class _TextOutputFormatter(_OutputFormatter):
|
||||
def common(self, data, params):
|
||||
if params.get("showMeta", 0):
|
||||
meta = data.meta
|
||||
for category in meta.directoryCategories:
|
||||
print "directoryCategory: %s" % \
|
||||
self.boil(category["fullViewableName"])
|
||||
for attr in [node for node in dir(meta) if \
|
||||
node <> "directoryCategories" and node[:2] <> '__']:
|
||||
print "%s:" % attr, self.boil(getattr(meta, attr))
|
||||
|
||||
def doGoogleSearch(self, data, params):
|
||||
results = data.results
|
||||
if params.get("feelingLucky", 0):
|
||||
results = results[:1]
|
||||
if params.get("reverseOrder", 0):
|
||||
results.reverse()
|
||||
for result in results:
|
||||
for attr in dir(result):
|
||||
if attr == "directoryCategory":
|
||||
print "directoryCategory:", \
|
||||
self.boil(result.directoryCategory["fullViewableName"])
|
||||
elif attr[:2] <> '__':
|
||||
print "%s:" % attr, self.boil(getattr(result, attr))
|
||||
print
|
||||
self.common(data, params)
|
||||
|
||||
def doGetCachedPage(self, data, params):
|
||||
print data
|
||||
self.common(data, params)
|
||||
|
||||
doSpellingSuggestion = doGetCachedPage
|
||||
|
||||
def _makeFormatter(outputFormat):
|
||||
classname = "_%sOutputFormatter" % outputFormat.capitalize()
|
||||
return globals()[classname]()
|
||||
|
||||
def _output(results, params):
|
||||
formatter = _makeFormatter(params.get("outputFormat", "text"))
|
||||
outputmethod = getattr(formatter, params["func"])
|
||||
outputmethod(results, params)
|
||||
|
||||
def main(argv):
|
||||
"""
|
||||
Command-line interface.
|
||||
"""
|
||||
if not argv:
|
||||
_usage()
|
||||
return
|
||||
q = None
|
||||
func = None
|
||||
http_proxy = None
|
||||
license_key = None
|
||||
feelingLucky = 0
|
||||
showMeta = 0
|
||||
reverseOrder = 0
|
||||
runTest = 0
|
||||
outputFormat = "text"
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "s:c:p:k:lmrx:hvt1",
|
||||
["search=", "cache=", "spelling=", "key=", "lucky", "meta",
|
||||
"reverse", "proxy=", "help", "version", "test"])
|
||||
except getopt.GetoptError:
|
||||
_usage()
|
||||
sys.exit(2)
|
||||
for opt, arg in opts:
|
||||
if opt in ("-s", "--search"):
|
||||
q = arg
|
||||
func = "doGoogleSearch"
|
||||
elif opt in ("-c", "--cache"):
|
||||
q = arg
|
||||
func = "doGetCachedPage"
|
||||
elif opt in ("-p", "--spelling"):
|
||||
q = arg
|
||||
func = "doSpellingSuggestion"
|
||||
elif opt in ("-k", "--key"):
|
||||
license_key = arg
|
||||
elif opt in ("-l", "-1", "--lucky"):
|
||||
feelingLucky = 1
|
||||
elif opt in ("-m", "--meta"):
|
||||
showMeta = 1
|
||||
elif opt in ("-r", "--reverse"):
|
||||
reverseOrder = 1
|
||||
elif opt in ("-x", "--proxy"):
|
||||
http_proxy = arg
|
||||
elif opt in ("-h", "--help"):
|
||||
_usage()
|
||||
elif opt in ("-v", "--version"):
|
||||
_version()
|
||||
elif opt in ("-t", "--test"):
|
||||
runTest = 1
|
||||
if runTest:
|
||||
setLicense(license_key)
|
||||
setProxy(http_proxy)
|
||||
_test()
|
||||
if args and not q:
|
||||
q = args[0]
|
||||
func = "doGoogleSearch"
|
||||
if func:
|
||||
results = globals()[func]( q, http_proxy=http_proxy,
|
||||
license_key=license_key )
|
||||
_output(results, locals())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
@@ -0,0 +1,5 @@
|
||||
sugardir = $(pythondir)/sugar/session
|
||||
sugar_PYTHON = \
|
||||
__init__.py \
|
||||
session.py \
|
||||
LogWriter.py
|
||||
@@ -0,0 +1,59 @@
|
||||
import os
|
||||
import signal
|
||||
from ConfigParser import ConfigParser
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
|
||||
from shell import Shell
|
||||
from sugar import env
|
||||
|
||||
class Session:
|
||||
def __init__(self):
|
||||
self._activity_processes = {}
|
||||
|
||||
def start(self):
|
||||
shell = Shell()
|
||||
shell.connect('close', self._shell_close_cb)
|
||||
shell.start()
|
||||
|
||||
activities = []
|
||||
activities_dirs = []
|
||||
|
||||
for data_dir in env.get_data_dirs():
|
||||
act_dir = os.path.join(data_dir, env.get_activities_dir())
|
||||
activities_dirs.append(act_dir)
|
||||
|
||||
activities_dirs.append(os.path.join(env.get_user_dir(), 'activities'))
|
||||
|
||||
for activities_dir in activities_dirs:
|
||||
if os.path.isdir(activities_dir):
|
||||
for filename in os.listdir(activities_dir):
|
||||
if filename.endswith(".activity"):
|
||||
path = os.path.join(activities_dir, filename)
|
||||
cp = ConfigParser()
|
||||
cp.read([path])
|
||||
python_class = cp.get('Activity', "python_class")
|
||||
activities.append(python_class)
|
||||
|
||||
for activity in activities:
|
||||
args = [ 'python', '-m', activity ]
|
||||
pid = os.spawnvp(os.P_NOWAIT, 'python', args)
|
||||
self._activity_processes[activity] = pid
|
||||
|
||||
try:
|
||||
gtk.main()
|
||||
except KeyboardInterrupt:
|
||||
print 'Ctrl+C pressed, exiting...'
|
||||
self.shutdown()
|
||||
|
||||
def _shell_close_cb(self, shell):
|
||||
self.shutdown()
|
||||
|
||||
def shutdown(self):
|
||||
# FIXME Obviously we want to notify the activities to
|
||||
# shutt down rather then killing them down forcefully.
|
||||
for name in self._activity_processes.keys():
|
||||
print 'Shutting down %s' % (name)
|
||||
os.kill(self._activity_processes[name], signal.SIGTERM)
|
||||
Executable
+516
@@ -0,0 +1,516 @@
|
||||
import dbus
|
||||
import dbus.service
|
||||
import dbus.glib
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import pango
|
||||
import gobject
|
||||
|
||||
import sugar.util
|
||||
from sugar.chat.ChatWindow import ChatWindow
|
||||
from sugar.chat.GroupChat import GroupChat
|
||||
from sugar.session.LogWriter import LogWriter
|
||||
|
||||
from Owner import ShellOwner
|
||||
from StartPage import StartPage
|
||||
from WindowManager import WindowManager
|
||||
from PresenceWindow import PresenceWindow
|
||||
|
||||
class ActivityHost(dbus.service.Object):
|
||||
|
||||
def __init__(self, activity_container, activity_name, default_type, activity_id = None):
|
||||
self.activity_name = activity_name
|
||||
self.ellipsize_tab = False
|
||||
|
||||
self.activity_container = activity_container
|
||||
|
||||
if activity_id is None:
|
||||
self.activity_id = sugar.util.unique_id()
|
||||
else:
|
||||
self.activity_id = activity_id
|
||||
self._default_type = default_type
|
||||
|
||||
self.dbus_object_name = "/com/redhat/Sugar/Shell/Activities/%s" % self.activity_id
|
||||
|
||||
dbus.service.Object.__init__(self, activity_container.service, self.dbus_object_name)
|
||||
self.socket = gtk.Socket()
|
||||
self.socket.set_data("sugar-activity", self)
|
||||
self.socket.show()
|
||||
|
||||
hbox = gtk.HBox(False, 4);
|
||||
|
||||
self.tab_activity_image = gtk.Image()
|
||||
self.tab_activity_image.set_from_stock(gtk.STOCK_CONVERT, gtk.ICON_SIZE_MENU)
|
||||
hbox.pack_start(self.tab_activity_image)
|
||||
#self.tab_activity_image.show()
|
||||
|
||||
self.label_hbox = gtk.HBox(False, 4);
|
||||
self.label_hbox.connect("style-set", self.__tab_label_style_set_cb)
|
||||
hbox.pack_start(self.label_hbox)
|
||||
|
||||
self.tab_label = gtk.Label(self.activity_name)
|
||||
self.tab_label.set_single_line_mode(True)
|
||||
self.tab_label.set_alignment(0.0, 0.5)
|
||||
self.tab_label.set_padding(0, 0)
|
||||
self.tab_label.show()
|
||||
|
||||
close_image = gtk.Image()
|
||||
close_image.set_from_stock (gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
|
||||
close_image.show()
|
||||
|
||||
self.tab_close_button = gtk.Button()
|
||||
rcstyle = gtk.RcStyle();
|
||||
rcstyle.xthickness = rcstyle.ythickness = 0;
|
||||
self.tab_close_button.modify_style (rcstyle);
|
||||
self.tab_close_button.add(close_image)
|
||||
self.tab_close_button.set_relief(gtk.RELIEF_NONE)
|
||||
self.tab_close_button.set_focus_on_click(False)
|
||||
self.tab_close_button.connect("clicked", self.tab_close_button_clicked)
|
||||
|
||||
self.label_hbox.pack_start(self.tab_label)
|
||||
self.label_hbox.pack_start(self.tab_close_button, False, False, 0)
|
||||
self.label_hbox.show()
|
||||
|
||||
hbox.show()
|
||||
|
||||
self._create_chat()
|
||||
|
||||
notebook = self.activity_container.notebook
|
||||
index = notebook.append_page(self.socket, hbox)
|
||||
notebook.set_current_page(index)
|
||||
|
||||
def _create_chat(self):
|
||||
self._group_chat = GroupChat(self)
|
||||
|
||||
def get_chat(self):
|
||||
return self._group_chat
|
||||
|
||||
def get_default_type(self):
|
||||
return self._default_type
|
||||
|
||||
def __close_button_clicked_reply_cb(self):
|
||||
pass
|
||||
|
||||
def __close_button_clicked_error_cb(self, error):
|
||||
pass
|
||||
|
||||
def publish(self):
|
||||
self._group_chat.publish()
|
||||
self.peer_service.publish()
|
||||
|
||||
def tab_close_button_clicked(self, button):
|
||||
self.peer_service.close_from_user(reply_handler = self.__close_button_clicked_reply_cb, \
|
||||
error_handler = self.__close_button_clicked_error_cb)
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="", \
|
||||
out_signature="t")
|
||||
def get_host_xembed_id(self):
|
||||
window_id = self.socket.get_id()
|
||||
#print "window_id = %d"%window_id
|
||||
return window_id
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="ss", \
|
||||
out_signature="")
|
||||
def set_peer_service_name(self, peer_service_name, peer_object_name):
|
||||
#print "peer_service_name = %s, peer_object_name = %s"%(peer_service_name, peer_object_name)
|
||||
self.__peer_service_name = peer_service_name
|
||||
self.__peer_object_name = peer_object_name
|
||||
self.peer_service = dbus.Interface(self.activity_container.bus.get_object( \
|
||||
self.__peer_service_name, self.__peer_object_name), \
|
||||
"com.redhat.Sugar.Activity")
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="b", \
|
||||
out_signature="")
|
||||
def set_ellipsize_tab(self, ellipsize):
|
||||
self.ellipsize_tab = True
|
||||
self.update_tab_size()
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="b", \
|
||||
out_signature="")
|
||||
def set_can_close(self, can_close):
|
||||
if can_close:
|
||||
self.tab_close_button.show()
|
||||
else:
|
||||
self.tab_close_button.hide()
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="b", \
|
||||
out_signature="")
|
||||
def set_tab_show_icon(self, show_icon):
|
||||
if show_icon:
|
||||
self.tab_activity_image.show()
|
||||
else:
|
||||
self.tab_activity_image.hide()
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="b", \
|
||||
out_signature="")
|
||||
def set_has_changes(self, has_changes):
|
||||
if has_changes:
|
||||
attrs = pango.AttrList()
|
||||
attrs.insert(pango.AttrForeground(50000, 0, 0, 0, -1))
|
||||
attrs.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1))
|
||||
self.tab_label.set_attributes(attrs)
|
||||
else:
|
||||
self.tab_label.set_attributes(pango.AttrList())
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="s", \
|
||||
out_signature="")
|
||||
def set_tab_text(self, text):
|
||||
self.tab_label.set_text(text)
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="ayibiiii", \
|
||||
out_signature="")
|
||||
def set_tab_icon(self, data, colorspace, has_alpha, bits_per_sample, width, height, rowstride):
|
||||
#print "width=%d, height=%d"%(width, height)
|
||||
#print " data = ", data
|
||||
pixstr = ""
|
||||
for c in data:
|
||||
# Work around for a bug in dbus < 0.61 where integers
|
||||
# are not correctly marshalled
|
||||
if c < 0:
|
||||
c += 256
|
||||
pixstr += chr(c)
|
||||
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_data(pixstr, colorspace, has_alpha, bits_per_sample, width, height, rowstride)
|
||||
#print pixbuf
|
||||
self.tab_activity_image.set_from_pixbuf(pixbuf)
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \
|
||||
in_signature="", \
|
||||
out_signature="")
|
||||
def shutdown(self):
|
||||
#print "shutdown"
|
||||
for owner, activity in self.activity_container.activities[:]:
|
||||
if activity == self:
|
||||
self.activity_container.activities.remove((owner, activity))
|
||||
|
||||
for i in range(self.activity_container.notebook.get_n_pages()):
|
||||
child = self.activity_container.notebook.get_nth_page(i)
|
||||
if child == self.socket:
|
||||
#print "found child"
|
||||
self.activity_container.notebook.remove_page(i)
|
||||
break
|
||||
|
||||
del self
|
||||
|
||||
def get_host_activity_id(self):
|
||||
"""Real function that the shell should use for getting the
|
||||
activity's ID."""
|
||||
return self.activity_id
|
||||
|
||||
def get_id(self):
|
||||
"""Interface-type function to match activity.Activity's
|
||||
get_id() function."""
|
||||
return self.activity_id
|
||||
|
||||
def get_object_path(self):
|
||||
return self.dbus_object_name
|
||||
|
||||
def update_tab_size(self):
|
||||
if self.ellipsize_tab:
|
||||
self.tab_label.set_ellipsize(pango.ELLIPSIZE_END)
|
||||
|
||||
context = self.label_hbox.get_pango_context()
|
||||
font_desc = self.label_hbox.style.font_desc
|
||||
metrics = context.get_metrics(font_desc, context.get_language())
|
||||
char_width = metrics.get_approximate_digit_width()
|
||||
[w, h] = self.__get_close_icon_size()
|
||||
tab_width = 15 * pango.PIXELS(char_width) + 2 * w
|
||||
self.label_hbox.set_size_request(tab_width, -1);
|
||||
else:
|
||||
self.tab_label.set_ellipsize(pango.ELLIPSIZE_NONE)
|
||||
self.label_hbox.set_size_request(-1, -1)
|
||||
|
||||
def __get_close_icon_size(self):
|
||||
settings = self.label_hbox.get_settings()
|
||||
return gtk.icon_size_lookup_for_settings(settings, gtk.ICON_SIZE_MENU)
|
||||
|
||||
def __tab_label_style_set_cb(self, widget, previous_style):
|
||||
[w, h] = self.__get_close_icon_size()
|
||||
self.tab_close_button.set_size_request (w + 5, h + 2)
|
||||
self.update_tab_size()
|
||||
|
||||
class ActivityContainerSignalHelper(gobject.GObject):
|
||||
"""A gobject whose sole purpose is to distribute signals for
|
||||
an ActivityContainer object."""
|
||||
|
||||
__gsignals__ = {
|
||||
'local-activity-started': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
|
||||
'local-activity-ended': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
|
||||
}
|
||||
|
||||
def __init__(self, parent):
|
||||
gobject.GObject.__init__(self)
|
||||
self._parent = parent
|
||||
|
||||
def activity_started(self, activity_id):
|
||||
self.emit('local-activity-started', self._parent, activity_id)
|
||||
|
||||
def activity_ended(self, activity_id):
|
||||
self.emit('local-activity-ended', self._parent, activity_id)
|
||||
|
||||
class ActivityContainer(dbus.service.Object):
|
||||
|
||||
def __init__(self, service, bus):
|
||||
self.activities = []
|
||||
|
||||
self.bus = bus
|
||||
self.service = service
|
||||
|
||||
self._signal_helper = ActivityContainerSignalHelper(self)
|
||||
|
||||
dbus.service.Object.__init__(self, self.service, "/com/redhat/Sugar/Shell/ActivityContainer")
|
||||
bus.add_signal_receiver(self.name_owner_changed, dbus_interface = "org.freedesktop.DBus", signal_name = "NameOwnerChanged")
|
||||
|
||||
self.window = gtk.Window()
|
||||
self.window.connect("key-press-event", self.__key_press_event_cb)
|
||||
self.window.set_title("OLPC Sugar")
|
||||
|
||||
self._fullscreen = False
|
||||
|
||||
self.notebook = gtk.Notebook()
|
||||
|
||||
tab_label = gtk.Label("Everyone")
|
||||
self._start_page = StartPage(self._signal_helper)
|
||||
self.notebook.append_page(self._start_page, tab_label)
|
||||
self._start_page.show()
|
||||
|
||||
self.notebook.show()
|
||||
self.notebook.connect("switch-page", self.notebook_tab_changed)
|
||||
self.window.add(self.notebook)
|
||||
|
||||
self.window.connect("destroy", lambda w: gtk.main_quit())
|
||||
|
||||
self.current_activity = None
|
||||
|
||||
# Create our owner service
|
||||
self._owner = ShellOwner()
|
||||
|
||||
self._presence_window = PresenceWindow(self)
|
||||
self._presence_window.set_transient_for(self.window)
|
||||
self._presence_window.set_decorated(False)
|
||||
self._presence_window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
|
||||
self._presence_window.set_skip_taskbar_hint(True)
|
||||
|
||||
wm = WindowManager(self._presence_window)
|
||||
|
||||
wm.set_width(0.15, WindowManager.SCREEN_RELATIVE)
|
||||
wm.set_height(1.0, WindowManager.SCREEN_RELATIVE)
|
||||
wm.set_position(WindowManager.LEFT)
|
||||
wm.manage()
|
||||
|
||||
self._chat_window = ChatWindow()
|
||||
self._chat_window.set_transient_for(self.window)
|
||||
self._chat_window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
|
||||
self._chat_window.set_decorated(False)
|
||||
self._chat_window.set_skip_taskbar_hint(True)
|
||||
|
||||
self._chat_wm = WindowManager(self._chat_window)
|
||||
|
||||
self._chat_wm.set_width(0.5, WindowManager.SCREEN_RELATIVE)
|
||||
self._chat_wm.set_height(0.5, WindowManager.SCREEN_RELATIVE)
|
||||
self._chat_wm.set_position(WindowManager.TOP)
|
||||
self._chat_wm.manage()
|
||||
|
||||
def show(self):
|
||||
self.window.show()
|
||||
|
||||
def __focus_reply_cb(self):
|
||||
pass
|
||||
|
||||
def __focus_error_cb(self, error):
|
||||
pass
|
||||
|
||||
def set_current_activity(self, activity):
|
||||
self.current_activity = activity
|
||||
self._presence_window.set_activity(activity)
|
||||
|
||||
if activity:
|
||||
host_chat = activity.get_chat()
|
||||
self._chat_window.set_chat(host_chat)
|
||||
|
||||
# For some reason the substitution screw up window position
|
||||
self._chat_wm.update()
|
||||
|
||||
def notebook_tab_changed(self, notebook, page, page_number):
|
||||
new_activity = notebook.get_nth_page(page_number).get_data("sugar-activity")
|
||||
|
||||
if self.current_activity != None:
|
||||
if self.has_activity(self.current_activity):
|
||||
self.current_activity.peer_service.lost_focus(reply_handler = self.__focus_reply_cb, error_handler = self.__focus_error_cb)
|
||||
|
||||
#if self.has_activity(new_activity):
|
||||
self.set_current_activity(new_activity)
|
||||
|
||||
if self.current_activity != None:
|
||||
if self.has_activity(self.current_activity):
|
||||
self.current_activity.peer_service.got_focus(reply_handler = self.__focus_reply_cb, error_handler = self.__focus_error_cb)
|
||||
|
||||
|
||||
def has_activity(self, activity_to_check_for):
|
||||
for owner, activity in self.activities[:]:
|
||||
if activity_to_check_for == activity:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def name_owner_changed(self, service_name, old_service_name, new_service_name):
|
||||
#print "in name_owner_changed: svc=%s oldsvc=%s newsvc=%s"%(service_name, old_service_name, new_service_name)
|
||||
for owner, activity in self.activities[:]:
|
||||
if owner == old_service_name:
|
||||
activity_id = activity.get_host_activity_id()
|
||||
self._signal_helper.activity_ended(activity_id)
|
||||
self.activities.remove((owner, activity))
|
||||
#self.__print_activities()
|
||||
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer", \
|
||||
in_signature="ss", \
|
||||
out_signature="s", \
|
||||
sender_keyword="sender")
|
||||
def add_activity(self, activity_name, default_type, sender):
|
||||
#print "hello world, activity_name = '%s', sender = '%s'"%(activity_name, sender)
|
||||
activity = ActivityHost(self, activity_name, default_type)
|
||||
self.activities.append((sender, activity))
|
||||
|
||||
activity_id = activity.get_host_activity_id()
|
||||
self._signal_helper.activity_started(activity_id)
|
||||
|
||||
self.current_activity = activity
|
||||
return activity_id
|
||||
|
||||
@dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer", \
|
||||
in_signature="sss", \
|
||||
sender_keyword="sender")
|
||||
def add_activity_with_id(self, activity_name, default_type, activity_id, sender):
|
||||
activity = ActivityHost(self, activity_name, default_type, activity_id)
|
||||
self.activities.append((sender, activity))
|
||||
activity_id = activity.get_host_activity_id()
|
||||
self._signal_helper.activity_started(activity_id)
|
||||
self.current_activity = activity
|
||||
|
||||
def __print_activities(self):
|
||||
print "__print_activities: %d activities registered" % len(self.activities)
|
||||
i = 0
|
||||
for owner, activity in self.activities:
|
||||
print " %d: owner=%s activity_object_name=%s" % (i, owner, activity.dbus_object_name)
|
||||
i += 1
|
||||
|
||||
def __key_press_event_cb(self, window, event):
|
||||
if event.keyval == gtk.keysyms.F11:
|
||||
if self._fullscreen:
|
||||
window.unfullscreen()
|
||||
self._fullscreen = False
|
||||
else:
|
||||
window.fullscreen()
|
||||
self._fullscreen = True
|
||||
|
||||
class ConsoleLogger(dbus.service.Object):
|
||||
def __init__(self):
|
||||
session_bus = dbus.SessionBus()
|
||||
bus_name = dbus.service.BusName('com.redhat.Sugar.Logger', bus=session_bus)
|
||||
object_path = '/com/redhat/Sugar/Logger'
|
||||
dbus.service.Object.__init__(self, bus_name, object_path)
|
||||
|
||||
self._window = gtk.Window()
|
||||
self._window.set_title("Console")
|
||||
self._window.set_default_size(640, 480)
|
||||
self._window.connect("delete_event", lambda w, e: w.hide_on_delete())
|
||||
|
||||
self._nb = gtk.Notebook()
|
||||
self._window.add(self._nb)
|
||||
self._nb.show()
|
||||
|
||||
self._consoles = {}
|
||||
|
||||
def set_parent_window(self, window):
|
||||
window.connect("key-press-event", self.__key_press_event_cb)
|
||||
self._window.connect("key-press-event", self.__key_press_event_cb)
|
||||
|
||||
def __key_press_event_cb(self, window, event):
|
||||
if event.keyval == gtk.keysyms.d and \
|
||||
event.state & gtk.gdk.CONTROL_MASK:
|
||||
if self._window.get_property('visible'):
|
||||
self._window.hide()
|
||||
else:
|
||||
self._window.show()
|
||||
|
||||
def _create_console(self, application):
|
||||
sw = gtk.ScrolledWindow()
|
||||
sw.set_policy(gtk.POLICY_AUTOMATIC,
|
||||
gtk.POLICY_AUTOMATIC)
|
||||
|
||||
console = gtk.TextView()
|
||||
console.set_wrap_mode(gtk.WRAP_WORD)
|
||||
|
||||
sw.add(console)
|
||||
console.show()
|
||||
|
||||
self._nb.append_page(sw, gtk.Label(application))
|
||||
sw.show()
|
||||
|
||||
return console
|
||||
|
||||
@dbus.service.method('com.redhat.Sugar.Logger')
|
||||
def log(self, application, message):
|
||||
if self._consoles.has_key(application):
|
||||
console = self._consoles[application]
|
||||
else:
|
||||
console = self._create_console(application)
|
||||
self._consoles[application] = console
|
||||
|
||||
buf = console.get_buffer()
|
||||
buf.insert(buf.get_end_iter(), message)
|
||||
|
||||
class Shell(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
'close': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
([])),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
def start(self):
|
||||
console = ConsoleLogger()
|
||||
|
||||
log_writer = LogWriter("Shell", False)
|
||||
log_writer.start()
|
||||
|
||||
session_bus = dbus.SessionBus()
|
||||
service = dbus.service.BusName("com.redhat.Sugar.Shell", bus=session_bus)
|
||||
|
||||
activity_container = ActivityContainer(service, session_bus)
|
||||
activity_container.window.connect('destroy', self.__activity_container_destroy_cb)
|
||||
activity_container.show()
|
||||
|
||||
wm = WindowManager(activity_container.window)
|
||||
wm.set_width(640, WindowManager.ABSOLUTE)
|
||||
wm.set_height(480, WindowManager.ABSOLUTE)
|
||||
wm.set_position(WindowManager.CENTER)
|
||||
wm.show()
|
||||
wm.manage()
|
||||
|
||||
console.set_parent_window(activity_container.window)
|
||||
|
||||
def __activity_container_destroy_cb(self, activity_container):
|
||||
self.emit('close')
|
||||
|
||||
if __name__ == "__main__":
|
||||
shell = Shell()
|
||||
shell.start()
|
||||
try:
|
||||
gtk.main()
|
||||
except KeyboardInterrupt:
|
||||
print 'Ctrl+c pressed, exiting...'
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import pwd
|
||||
import random
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gobject
|
||||
|
||||
def add_to_python_path(path):
|
||||
sys.path.insert(0, path)
|
||||
if os.environ.has_key('PYTHONPATH'):
|
||||
os.environ['PYTHONPATH'] += ':' + path
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = path
|
||||
|
||||
def start_dbus():
|
||||
curdir = os.path.dirname(__file__)
|
||||
args = "/bin/dbus-daemon --session --print-address".split()
|
||||
(dbus_pid, ign1, dbus_stdout, ign3) = gobject.spawn_async(args, flags=gobject.SPAWN_STDERR_TO_DEV_NULL, standard_output=True)
|
||||
dbus_file = os.fdopen(dbus_stdout)
|
||||
addr = dbus_file.readline()
|
||||
addr = addr.strip()
|
||||
print "dbus-daemon pid is %d, session bus address is %s" % (dbus_pid, addr)
|
||||
dbus_file.close()
|
||||
|
||||
os.environ["DBUS_SESSION_BUS_ADDRESS"] = addr
|
||||
|
||||
return dbus_pid
|
||||
|
||||
def stop_dbus(dbus_pid):
|
||||
try:
|
||||
print 'Closing dbus-daemon, pid %d' % (dbus_pid)
|
||||
os.kill(dbus_pid, 9)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
i = 0
|
||||
dbus_daemon_pid = None
|
||||
for arg in sys.argv:
|
||||
if arg == '--test-user':
|
||||
user = sys.argv[i + 1]
|
||||
user_dir = os.path.expanduser('~/.sugar-' + user)
|
||||
os.environ['SUGAR_NICK_NAME'] = user
|
||||
os.environ['SUGAR_USER_DIR'] = user_dir
|
||||
dbus_daemon_pid = start_dbus()
|
||||
started_dbus = True
|
||||
i += 1
|
||||
|
||||
if not os.environ.has_key("SUGAR_NICK_NAME"):
|
||||
nick = pwd.getpwuid(os.getuid())[0]
|
||||
if not nick or not len(nick):
|
||||
nick = "Guest %d" % random.randint(1, 10000)
|
||||
os.environ['SUGAR_NICK_NAME'] = nick
|
||||
os.environ['SUGAR_USER_DIR'] = os.path.expanduser('~/.sugar')
|
||||
|
||||
|
||||
curdir = os.path.abspath(os.path.dirname(__file__))
|
||||
basedir = os.path.dirname(curdir)
|
||||
|
||||
if os.path.isfile(os.path.join(basedir, 'sugar/__uninstalled__.py')):
|
||||
print 'Running sugar from ' + basedir + ' ...'
|
||||
add_to_python_path(basedir)
|
||||
add_to_python_path(os.path.join(basedir, 'shell'))
|
||||
add_to_python_path(os.path.join(basedir, 'activities/browser'))
|
||||
else:
|
||||
print 'Running the installed sugar...'
|
||||
|
||||
add_to_python_path(os.path.expanduser('~/.sugar/activities'))
|
||||
|
||||
print 'Redirecting output to the console, press ctrl+d to open it.'
|
||||
|
||||
from session.session import Session
|
||||
|
||||
session = Session()
|
||||
session.start()
|
||||
|
||||
if dbus_daemon_pid:
|
||||
stop_dbus(dbus_daemon_pid)
|
||||
Reference in New Issue
Block a user