More work on the window management refactor

This commit is contained in:
Marco Pesenti Gritti 2006-07-09 17:37:54 +02:00
parent d4cb9a2714
commit 1cc14e406a
17 changed files with 214 additions and 5774 deletions

View File

@ -37,6 +37,43 @@ class BrowserActivity(Activity):
self._notif_service = None
self._model = None
self.set_title("Web Page")
vbox = gtk.VBox()
self._notif_bar = NotificationBar()
vbox.pack_start(self._notif_bar, False)
self._notif_bar.connect('action', self.__notif_bar_action_cb)
self.embed = geckoembed.Embed()
self.embed.connect("title", self.__title_cb)
vbox.pack_start(self.embed)
self.embed.show()
self.embed.load_address(self.uri)
nav_toolbar = NavigationToolbar(self)
vbox.pack_start(nav_toolbar, False)
nav_toolbar.show()
self.add(vbox)
vbox.show()
logging.debug('Start presence service')
self._pservice = PresenceService.get_instance()
self._pservice.start()
logging.debug('Track browser activities')
self._pservice.connect('service-appeared', self._service_appeared_cb)
self._pservice.track_service_type(_BROWSER_ACTIVITY_TYPE)
self._pservice.track_service_type(LocalModel.SERVICE_TYPE)
# Join the shared activity if we were started from one
if self._initial_service:
logging.debug("BrowserActivity joining shared activity %s" %
self._initial_service.get_activity_id())
self._pservice.join_shared_activity(self._initial_service)
def _service_appeared_cb(self, pservice, buddy, service):
# Make sure the service is for our activity
if service.get_activity_id() != self._activity_id:
@ -78,43 +115,6 @@ class BrowserActivity(Activity):
self._notif_bar.set_icon('stock_shared-by-me')
self._notif_bar.show()
def on_connected_to_shell(self):
self.set_title("Web Page")
vbox = gtk.VBox()
self._notif_bar = NotificationBar()
vbox.pack_start(self._notif_bar, False)
self._notif_bar.connect('action', self.__notif_bar_action_cb)
self.embed = geckoembed.Embed()
self.embed.connect("title", self.__title_cb)
vbox.pack_start(self.embed)
self.embed.show()
self.embed.load_address(self.uri)
nav_toolbar = NavigationToolbar(self)
vbox.pack_start(nav_toolbar, False)
nav_toolbar.show()
self.add(vbox)
vbox.show()
logging.debug('Start presence service')
self._pservice = PresenceService.get_instance()
self._pservice.start()
logging.debug('Track browser activities')
self._pservice.connect('service-appeared', self._service_appeared_cb)
self._pservice.track_service_type(_BROWSER_ACTIVITY_TYPE)
self._pservice.track_service_type(LocalModel.SERVICE_TYPE)
# Join the shared activity if we were started from one
if self._initial_service:
logging.debug("BrowserActivity joining shared activity %s" % self._initial_service.get_activity_id())
self._pservice.join_shared_activity(self._initial_service)
def get_embed(self):
return self.embed

View File

@ -1,102 +0,0 @@
import dbus
import gobject
import gtk
from gettext import gettext as _
from sugar.chat.ChatWindow import ChatWindow
from sugar.chat.MeshChat import MeshChat
from ActivityHost import ActivityHost
from PresenceWindow import PresenceWindow
from WindowManager import WindowManager
from StartPage import StartPage
from Owner import ShellOwner
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)
self._current_activity = None
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")
# Create our owner service
self._owner = ShellOwner()
self._presence_window = PresenceWindow(self)
wm = WindowManager(self._presence_window)
wm.set_type(WindowManager.TYPE_POPUP)
wm.set_animation(WindowManager.ANIMATION_SLIDE_IN)
wm.set_geometry(0.02, 0.1, 0.25, 0.9)
wm.set_key(gtk.keysyms.F1)
self._chat_window = ChatWindow()
chat_wm = WindowManager(self._chat_window)
chat_wm.set_animation(WindowManager.ANIMATION_SLIDE_IN)
chat_wm.set_type(WindowManager.TYPE_POPUP)
chat_wm.set_geometry(0.28, 0.1, 0.5, 0.9)
chat_wm.set_key(gtk.keysyms.F1)
self._mesh_chat = MeshChat()
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)
else:
self._chat_window.set_chat(self._mesh_chat)
def name_owner_changed(self, 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))
@dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer")
def add_activity(self, default_type):
activity = ActivityHost(self._service, default_type)
self._activities.append(activity)
activity_id = activity.get_id()
self._signal_helper.activity_started(activity_id)
self.set_current_activity(activity)
return activity_id
@dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer")
def add_activity_with_id(self, default_type, activity_id):
activity = ActivityHost(self._service, default_type, activity_id)
self._activities.append(activity)
activity_id = activity.get_host_activity_id()
self._signal_helper.activity_started(activity_id)
self.current_activity = activity

View File

@ -1,130 +0,0 @@
import dbus
import gtk
import gobject
from sugar.chat.ActivityChat import ActivityChat
from WindowManager import WindowManager
import sugar.util
class ActivityHostSignalHelper(gobject.GObject):
__gsignals__ = {
'shared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
}
def __init__(self, parent):
gobject.GObject.__init__(self)
self._parent = parent
def emit_shared(self):
self.emit('shared')
class ActivityHost(dbus.service.Object):
def __init__(self, bus_name, default_type, activity_id = None):
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, bus_name, self.dbus_object_name)
self._signal_helper = ActivityHostSignalHelper(self)
self.peer_service = None
self._shared = False
self._create_chat()
def _create_chat(self):
self._activity_chat = ActivityChat(self)
def got_focus(self):
if self.peer_service != None:
self.peer_service.got_focus()
def lost_focus(self):
self.peer_service.lost_focus()
def get_chat(self):
return self._activity_chat
def get_default_type(self):
return self._default_type
def publish(self):
self._activity_chat.publish()
self.peer_service.publish()
def connect(self, signal, func):
self._signal_helper.connect(signal, func)
def get_id(self):
"""Interface-type function to match activity.Activity's
get_id() function."""
return self._activity_id
def default_type(self):
"""Interface-type function to match activity.Activity's
default_type() function."""
return self._default_type
def get_object_path(self):
return self.dbus_object_name
def get_shared(self):
"""Return True if this activity is shared, False if
it has not been shared yet."""
return self._shared
def _shared_signal(self):
self._shared = True
self._signal_helper.emit_shared()
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost")
def set_peer_service_name(self, peer_service_name, peer_object_name):
self.__peer_service_name = peer_service_name
self.__peer_object_name = peer_object_name
session_bus = dbus.SessionBus()
self.peer_service = dbus.Interface(session_bus.get_object( \
self.__peer_service_name, self.__peer_object_name), \
"com.redhat.Sugar.Activity")
session_bus.add_signal_receiver(self._shared_signal,
signal_name="ActivityShared",
dbus_interface="com.redhat.Sugar.Activity",
named_service=self.__peer_service_name,
path=self.__peer_object_name)
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost")
def set_has_changes(self, has_changes):
pass
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost")
def set_title(self, text):
pass
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost")
def set_icon(self, data, colorspace, has_alpha, bits_per_sample, width, height, rowstride):
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)
@dbus.service.method("com.redhat.Sugar.Shell.ActivityHost")
def shutdown(self):
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:
self.activity_container.notebook.remove_page(i)
break
del self

View File

@ -1,9 +1,6 @@
import gtk
import dbus
from WindowManager import WindowManager
from ActivityContainer import ActivityContainer
class ConsoleLogger(dbus.service.Object):
def __init__(self):
session_bus = dbus.SessionBus()
@ -21,10 +18,8 @@ class ConsoleLogger(dbus.service.Object):
self._consoles = {}
console_wm = WindowManager(self._window)
console_wm.set_type(WindowManager.TYPE_POPUP)
console_wm.set_geometry(0.1, 0.1, 0.8, 0.8)
console_wm.set_key(gtk.keysyms.F3)
def get_window(self):
return self._window
def _create_console(self, application):
sw = gtk.ScrolledWindow()
@ -52,4 +47,3 @@ class ConsoleLogger(dbus.service.Object):
buf = console.get_buffer()
buf.insert(buf.get_end_iter(), message)

View File

@ -16,11 +16,11 @@ class PresenceWindow(gtk.Window):
_MODEL_COL_BUDDY = 2
_MODEL_COL_VISIBLE = 3
def __init__(self, activity_container):
def __init__(self, shell):
gtk.Window.__init__(self)
self._activity_container = activity_container
self._activity = None
self._shell = shell
self._pservice = PresenceService.get_instance()
self._pservice.connect("buddy-appeared", self._on_buddy_appeared_cb)
@ -113,7 +113,7 @@ class PresenceWindow(gtk.Window):
vbox.show()
def _share_button_clicked_cb(self, button):
self._activity_container.current_activity.publish()
self._shell.get_current_activity().publish()
def _on_buddyList_buddy_selected(self, view, *args):
(model, aniter) = view.get_selection().get_selected()

View File

@ -1,13 +1,36 @@
import dbus
import gtk
import gobject
import wnck
from sugar.LogWriter import LogWriter
from WindowManager import WindowManager
from ConsoleLogger import ConsoleLogger
from ActivityContainer import ActivityContainer
from ActivityRegistry import ActivityRegistry
from HomeWindow import HomeWindow
from sugar import keybindings
from sugar.activity import Activity
from PresenceWindow import PresenceWindow
from sugar.chat.ActivityChat import ActivityChat
class ShellDbusService(dbus.service.Object):
def __init__(self, shell, bus_name):
dbus.service.Object.__init__(self, bus_name, '/com/redhat/Sugar/Shell')
self._shell = shell
def __toggle_people_idle(self):
self._shell.toggle_people()
@dbus.service.method('com.redhat.Sugar.Shell')
def toggle_people(self):
gobject.idle_add(self.__toggle_people_idle)
@dbus.service.method('com.redhat.Sugar.Shell')
def toggle_home(self):
self._shell.toggle_home()
@dbus.service.method('com.redhat.Sugar.Shell')
def toggle_console(self):
self._shell.toggle_console()
class Shell(gobject.GObject):
__gsignals__ = {
@ -19,31 +42,74 @@ class Shell(gobject.GObject):
gobject.GObject.__init__(self)
def start(self):
console = ConsoleLogger()
self._console = ConsoleLogger()
keybindings.setup_global_keys(self._console.get_window(), self)
log_writer = LogWriter("Shell", False)
log_writer.start()
session_bus = dbus.SessionBus()
bus_name = dbus.service.BusName('com.redhat.Sugar.Shell', bus=session_bus)
ShellDbusService(self, bus_name)
self._registry = ActivityRegistry()
root_window = gtk.Window()
root_window.set_title('Sugar')
wm = WindowManager(root_window)
wm.set_type(WindowManager.TYPE_ROOT)
wm.show()
self._home_window = HomeWindow(self)
keybindings.setup_global_keys(self._home_window, self)
self._home_window.show()
home_window = HomeWindow(self)
home_window.set_transient_for(root_window)
wm = WindowManager(home_window)
wm.set_type(WindowManager.TYPE_POPUP)
wm.set_animation(WindowManager.ANIMATION_SLIDE_IN)
wm.set_geometry(0.1, 0.1, 0.8, 0.8)
wm.set_key(gtk.keysyms.F2)
wm.show()
self._presence_window = PresenceWindow(self)
self._presence_window.set_skip_taskbar_hint(True)
keybindings.setup_global_keys(self._presence_window, self)
session_bus = dbus.SessionBus()
service = dbus.service.BusName("com.redhat.Sugar.Shell", bus=session_bus)
activity_container = ActivityContainer(service, session_bus)
self._chat_windows = {}
def _toggle_window_visibility(self, window):
if window.get_property('visible'):
window.hide()
else:
window.show()
def toggle_home(self):
self._toggle_window_visibility(self._home_window)
def get_current_activity(self):
window = wnck.screen_get_default().get_active_window()
if window and not window.is_skip_tasklist():
bus = dbus.SessionBus()
xid = window.get_xid()
service = Activity.ACTIVITY_SERVICE_NAME + "%s" % xid
path = Activity.ACTIVITY_SERVICE_PATH + "/%s" % xid
proxy_obj = bus.get_object(service, path)
return dbus.Interface(proxy_obj, 'com.redhat.Sugar.Activity')
else:
return None
def toggle_people(self):
activity = self.get_current_activity()
if activity:
activity_id = activity.get_id()
if not self._chat_windows.has_key(activity_id):
window = gtk.Window()
window.set_skip_taskbar_hint(True)
keybindings.setup_global_keys(window, self)
chat = ActivityChat(activity)
window.add(chat)
chat.show()
self._chat_windows[activity_id] = window
self._toggle_window_visibility(self._chat_windows[activity_id])
self._presence_window.set_activity(activity)
self._toggle_window_visibility(self._presence_window)
else:
self._presence_window.hide()
for window in self._chat_windows.values():
window.hide()
def toggle_console(self):
self._toggle_window_visibility(self._console.get_window())
def get_registry(self):
return self._registry

View File

@ -1,314 +0,0 @@
import pygtk
pygtk.require('2.0')
import gtk
import pango
import cgi
import xml.sax.saxutils
import gobject
import socket
import dbus_bindings
from google import google
from sugar.presence.PresenceService import PresenceService
from sugar.activity import Activity
from gettext import gettext as _
_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_id):
self.search_id = activity_id
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)
success = False
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'
try:
data = google.doGoogleSearch(search_text)
success = True
except socket.gaierror, exc:
if exc[0] == -3: # Temporary failure in name resolution
errdlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO,
gtk.BUTTONS_OK, "There appears to be no network connection.")
errdlg.connect("response", lambda d, e: d.destroy())
errdlg.connect("close", lambda d, e: d.destroy())
errdlg.show()
if success == True:
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('&quot;', '"')
title = title.replace('&amp;', '&')
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_id() == helper.search_id:
helper.found = True
return True
return False
def add_activity(self, buddy, service):
# Web Activity check
activity_id = service.get_activity_id()
if activity_id is None:
return
# Don't show dupes
helper = SearchHelper(activity_id)
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, activity_controller, model):
gtk.TreeView.__init__(self, model)
self._owner = None
self._activity_controller = activity_controller
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):
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)
return
if not self._owner:
raise RuntimeError("We don't have an owner yet!")
# If the activity is already started, switch to it
service_act_id = service.get_activity_id()
if service_act_id and self._activity_controller.have_activity(service_act_id):
self._activity_controller.switch_to_activity(service_act_id)
return
Activity.create('com.redhat.Sugar.BrowserActivity', service, [ address ])
class StartPage(gtk.HBox):
def __init__(self, activity_controller, 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(activity_controller, 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 service (activity %s of type %s)" % (service.get_activity_id(), service.get_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

View File

@ -1,208 +0,0 @@
import time
import logging
import gtk
import gobject
DEFAULT_WIDTH = 640
DEFAULT_HEIGHT = 480
SLIDING_TIME = 0.8
class SlidingHelper:
IN = 0
OUT = 1
def __init__(self, manager, direction):
self._direction = direction
self._cur_time = time.time()
self._target_time = self._cur_time + SLIDING_TIME
self._manager = manager
self._start = True
self._end = False
(x, y, width, height) = manager.get_geometry()
self._orig_y = y
if direction == SlidingHelper.IN:
self._target_y = y
manager.set_geometry(x, y - height, width, height)
else:
self._target_y = y - height
def get_direction(self):
return self._direction
def is_start(self):
return self._start
def is_end(self):
return self._end
def get_next_y(self):
self._start = False
(x, y, width, height) = self._manager.get_geometry()
old_time = self._cur_time
self._cur_time = time.time()
remaining = self._target_time - self._cur_time
if remaining <= 0 or \
(y > self._target_y and self._direction == SlidingHelper.IN) or \
(y < self._target_y and self._direction == SlidingHelper.OUT):
self._end = True
y = self._orig_y
else:
approx_time_step = float(self._cur_time - old_time)
approx_n_steps = remaining / approx_time_step
step = (self._target_y - y) / approx_n_steps
y += step
return y
class WindowManager:
__managers_list = []
TYPE_ROOT = 0
TYPE_ACTIVITY = 1
TYPE_POPUP = 2
ANIMATION_NONE = 0
ANIMATION_SLIDE_IN = 1
def __init__(self, window):
self._window = window
self._window_type = WindowManager.TYPE_ACTIVITY
self._animation = WindowManager.ANIMATION_NONE
self._key = 0
self._animating = False
window.connect("key-press-event", self.__key_press_event_cb)
WindowManager.__managers_list.append(self)
def __key_press_event_cb(self, window, event):
# FIXME we should fix this to work also while animating
if self._animating:
return False
for manager in WindowManager.__managers_list:
if event.keyval == manager._key:
if manager._window.get_property('visible'):
manager.hide()
else:
manager.show()
def get_geometry(self):
return (self._x, self._y, self._width, self._height)
def set_geometry(self, x, y, width, height):
if self._window_type == WindowManager.TYPE_ACTIVITY:
logging.error('The geometry will be ignored for activity windows')
self._x = x
self._y = y
self._width = width
self._height = height
def set_animation(self, animation):
self._animation = animation
def set_type(self, window_type):
self._window_type = window_type
def set_key(self, key):
self._key = key
def show(self):
self._update_hints()
self._update_size()
if self._animation == WindowManager.ANIMATION_SLIDE_IN:
self._slide_in()
else:
self._update_position()
self._window.show()
def hide(self):
if self._animation == WindowManager.ANIMATION_SLIDE_IN:
self._slide_out()
else:
self._window.hide()
def _get_screen_dimensions(self):
screen_width = DEFAULT_WIDTH
screen_height = DEFAULT_HEIGHT
for manager in WindowManager.__managers_list:
if manager._window_type == WindowManager.TYPE_ROOT:
screen_width = manager._window.allocation.width
screen_height = manager._window.allocation.height
return (screen_width, screen_height)
def _get_screen_position(self):
result = (0, 0)
for manager in WindowManager.__managers_list:
if manager._window_type == WindowManager.TYPE_ROOT:
result = manager._window.get_position()
return result
def _transform_position(self):
(screen_width, screen_height) = self._get_screen_dimensions()
(screen_x, screen_y) = self._get_screen_position()
x = int(screen_width * self._x) + screen_x
y = int(screen_height * self._y) + screen_y
return (x, y)
def _transform_dimensions(self):
(screen_width, screen_height) = self._get_screen_dimensions()
width = int(screen_width * self._width)
height = int(screen_height * self._height)
return (width, height)
def _update_hints(self):
if self._window_type == WindowManager.TYPE_POPUP:
self._window.set_decorated(False)
self._window.set_skip_taskbar_hint(True)
def _update_size(self):
if (self._window_type == WindowManager.TYPE_ACTIVITY) or \
(self._window_type == WindowManager.TYPE_ROOT):
self._window.resize(DEFAULT_WIDTH, DEFAULT_HEIGHT)
else:
(width, height) = self._transform_dimensions()
self._window.resize(width, height)
def _update_position(self):
if self._window_type == WindowManager.TYPE_POPUP:
(x, y) = self._transform_position()
self._window.move(x, y)
def __slide_timeout_cb(self, helper):
start = helper.is_start()
self._y = helper.get_next_y()
self._update_position()
if start and helper.get_direction() == SlidingHelper.IN:
self._window.show()
elif helper.is_end() and helper.get_direction() == SlidingHelper.OUT:
self._window.hide()
self._animating = not helper.is_end()
return not helper.is_end()
def _slide_in(self):
helper = SlidingHelper(self, SlidingHelper.IN)
gobject.idle_add(self.__slide_timeout_cb, helper)
def _slide_out(self):
helper = SlidingHelper(self, SlidingHelper.OUT)
gobject.idle_add(self.__slide_timeout_cb, helper)

View File

@ -1,85 +0,0 @@
"""
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

View File

@ -1,6 +0,0 @@
googledir = $(pkgdatadir)/shell/google
google_PYTHON = \
__init__.py \
google.py \
GoogleSOAPFacade.py \
SOAP.py

File diff suppressed because it is too large Load Diff

View File

@ -1,638 +0,0 @@
"""
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:])

View File

@ -9,6 +9,9 @@ import pygtk
pygtk.require('2.0')
import gobject
# FIXME How to pick a good display number
XEPHYR_DISPLAY = 100
def add_to_python_path(path):
sys.path.insert(0, path)
if os.environ.has_key('PYTHONPATH'):
@ -23,7 +26,6 @@ def start_dbus():
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
@ -37,6 +39,20 @@ def stop_dbus(dbus_pid):
except OSError:
pass
def start_xephyr():
command = 'Xephyr :%d -ac -screen 640x480' % (XEPHYR_DISPLAY)
xephyr_pid = os.spawnvp(os.P_NOWAIT, 'Xephyr', command.split())
def stop_xephyr():
os.kill(xephyr_pid)
def start_matchbox():
command = 'matchbox-window-manager -use_titlebar no'
xephyr_pid = os.spawnvp(os.P_NOWAIT, 'matchbox-window-manager', command.split())
def stop_matchbox():
os.kill(xephyr_pid)
i = 0
dbus_daemon_pid = None
for arg in sys.argv:
@ -56,7 +72,6 @@ if not os.environ.has_key("SUGAR_NICK_NAME"):
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)
@ -69,6 +84,10 @@ else:
add_to_python_path(os.path.join(sugar.env.get_data_dir(), 'shell'))
print 'Running the installed sugar...'
start_xephyr()
os.environ['DISPLAY'] = ":%d" % (XEPHYR_DISPLAY)
start_matchbox()
print 'Redirecting output to the console, press Ctrl+Down to open it.'
from session.session import Session
@ -78,3 +97,6 @@ session.start()
if dbus_daemon_pid:
stop_dbus(dbus_daemon_pid)
stop_matchbox()
stop_xephyr()

View File

@ -9,6 +9,8 @@ pygtk.require('2.0')
import gtk, gobject
from sugar.LogWriter import LogWriter
from sugar import keybindings
import sugar.util
SHELL_SERVICE_NAME = "com.redhat.Sugar.Shell"
SHELL_SERVICE_PATH = "/com/redhat/Sugar/Shell"
@ -16,12 +18,6 @@ SHELL_SERVICE_PATH = "/com/redhat/Sugar/Shell"
ACTIVITY_SERVICE_NAME = "com.redhat.Sugar.Activity"
ACTIVITY_SERVICE_PATH = "/com/redhat/Sugar/Activity"
ON_CONNECTED_TO_SHELL_CB = "connected_to_shell"
ON_DISCONNECTED_FROM_SHELL_CB = "disconnected_from_shell"
ON_RECONNECTED_TO_SHELL_CB = "reconnected_to_shell"
ON_CLOSE_FROM_USER_CB = "close_from_user"
ON_LOST_FOCUS_CB = "lost_focus"
ON_GOT_FOCUS_CB = "got_focus"
ON_PUBLISH_CB = "publish"
def get_path(activity_name):
@ -65,15 +61,11 @@ class ActivityFactory(dbus.service.Object):
service = Service.deserialize(serialized_service)
activity = self._class(args)
gobject.idle_add(self._start_activity_cb, activity, service)
@dbus.service.method("com.redhat.Sugar.ActivityFactory")
def create(self):
self.create_with_service(None, [])
def _start_activity_cb(self, activity, service):
activity.connect_to_shell(service)
def create(activity_name, service = None, args = None):
"""Create a new activity from his name."""
bus = dbus.SessionBus()
@ -118,29 +110,19 @@ class ActivityDbusService(dbus.service.Object):
The dbus service is separate from the actual Activity object so that we can
tightly control what stuff passes through the dbus python bindings."""
_ALLOWED_CALLBACKS = [ON_CONNECTED_TO_SHELL_CB, ON_DISCONNECTED_FROM_SHELL_CB, \
ON_RECONNECTED_TO_SHELL_CB, ON_CLOSE_FROM_USER_CB, ON_LOST_FOCUS_CB, \
ON_GOT_FOCUS_CB, ON_PUBLISH_CB]
_ALLOWED_CALLBACKS = [ON_PUBLISH_CB]
def __init__(self, activity):
def __init__(self, xid, activity):
self._activity = activity
self._activity_id = None
self._activity_object = None
self._service = None
self._bus = dbus.SessionBus()
self._bus.add_signal_receiver(self.name_owner_changed, dbus_interface = "org.freedesktop.DBus", signal_name = "NameOwnerChanged")
self._callbacks = {}
for cb in self._ALLOWED_CALLBACKS:
self._callbacks[cb] = None
def __del__(self):
if self._activity_id:
del self._service
del self._activity_container
del self._activity_conainer_object
del self._activity_object
self._bus.remove_signal_receiver(self.name_owner_changed, dbus_interface="org.freedesktop.DBus", signal_name="NameOwnerChanged")
del self._bus
bus = dbus.SessionBus()
service_name = ACTIVITY_SERVICE_NAME + "%s" % xid
object_path = ACTIVITY_SERVICE_PATH + "/%s" % xid
service = dbus.service.BusName(service_name, bus=bus)
dbus.service.Object.__init__(self, service, object_path)
def register_callback(self, name, callback):
if name not in self._ALLOWED_CALLBACKS:
@ -159,98 +141,33 @@ class ActivityDbusService(dbus.service.Object):
if name in self._ALLOWED_CALLBACKS and self._callbacks[name]:
gobject.idle_add(self._call_callback_cb, self._callbacks[name], *args)
def connect_to_shell(self, service=None):
"""Register with the shell via dbus, getting an activity ID and
and XEMBED window ID in which to display the Activity."""
self._activity_container_object = self._bus.get_object(SHELL_SERVICE_NAME, \
SHELL_SERVICE_PATH + "/ActivityContainer")
self._activity_container = dbus.Interface(self._activity_container_object, \
SHELL_SERVICE_NAME + ".ActivityContainer")
if service is None:
self._activity_id = self._activity_container.add_activity(self._activity.default_type())
else:
self._activity_id = service.get_activity_id()
self._activity_container.add_activity_with_id(self._activity.default_type(), self._activity_id)
self._object_path = SHELL_SERVICE_PATH + "/Activities/%s" % self._activity_id
print "ActivityDbusService: object path is '%s'" % self._object_path
self._activity_object = dbus.Interface(self._bus.get_object(SHELL_SERVICE_NAME, self._object_path), \
SHELL_SERVICE_NAME + ".ActivityHost")
# Now let us register a peer service so the Shell can poke it
self._peer_service_name = ACTIVITY_SERVICE_NAME + "%s" % self._activity_id
self._peer_object_path = ACTIVITY_SERVICE_PATH + "/%s" % self._activity_id
self._service = dbus.service.BusName(self._peer_service_name, bus=self._bus)
dbus.service.Object.__init__(self, self._service, self._peer_object_path)
self._activity_object.set_peer_service_name(self._peer_service_name, self._peer_object_path)
self._call_callback(ON_CONNECTED_TO_SHELL_CB, self._activity_object, self._activity_id, service)
def _shutdown_reply_cb(self):
"""Shutdown was successful, tell the Activity that we're disconnected."""
self._call_callback(ON_DISCONNECTED_FROM_SHELL_CB)
def _shutdown_error_cb(self, error):
print "ActivityDbusService: error during shutdown - '%s'" % error
def shutdown(self):
"""Notify the shell that we are shutting down."""
self._activity_object.shutdown(reply_handler=self._shutdown_reply_cb, error_handler=self._shutdown_error_cb)
def name_owner_changed(self, service_name, old_service_name, new_service_name):
"""Handle dbus NameOwnerChanged signals."""
if not self._activity_id:
# Do nothing if we're not connected yet
return
if service_name == SHELL_SERVICE_NAME and not len(new_service_name):
self._call_callback(ON_DISCONNECTED_FROM_SHELL_CB)
elif service_name == SHELL_SERVICE_NAME and not len(old_service_name):
self._call_callback(ON_RECONNECTED_TO_SHELL_CB)
@dbus.service.method(ACTIVITY_SERVICE_NAME)
def lost_focus(self):
"""Called by the shell to notify us that we've lost focus."""
self._call_callback(ON_LOST_FOCUS_CB)
@dbus.service.method(ACTIVITY_SERVICE_NAME)
def got_focus(self):
"""Called by the shell to notify us that the user gave us focus."""
self._call_callback(ON_GOT_FOCUS_CB)
@dbus.service.method(ACTIVITY_SERVICE_NAME)
def close_from_user(self):
"""Called by the shell to notify us that the user closed us."""
self._call_callback(ON_CLOSE_FROM_USER_CB)
@dbus.service.method(ACTIVITY_SERVICE_NAME)
def publish(self):
"""Called by the shell to request the activity to publish itself on the network."""
self._call_callback(ON_PUBLISH_CB)
@dbus.service.signal(ACTIVITY_SERVICE_NAME)
def ActivityShared(self):
pass
@dbus.service.method(ACTIVITY_SERVICE_NAME)
def get_id(self):
"""Get the activity identifier"""
self._activity.get_id()
@dbus.service.method(ACTIVITY_SERVICE_NAME)
def get_shared(self):
"""Get the activity identifier"""
return self._activity.get_shared()
class Activity(gtk.Window):
"""Base Activity class that all other Activities derive from."""
def __init__(self, default_type):
def __init__(self, default_type, activity_id = None):
gtk.Window.__init__(self)
self._dbus_service = self._get_new_dbus_service()
self._dbus_service.register_callback(ON_CONNECTED_TO_SHELL_CB, self._internal_on_connected_to_shell_cb)
self._dbus_service.register_callback(ON_DISCONNECTED_FROM_SHELL_CB, self._internal_on_disconnected_from_shell_cb)
self._dbus_service.register_callback(ON_RECONNECTED_TO_SHELL_CB, self._internal_on_reconnected_to_shell_cb)
self._dbus_service.register_callback(ON_CLOSE_FROM_USER_CB, self._internal_on_close_from_user_cb)
self._dbus_service.register_callback(ON_PUBLISH_CB, self._internal_on_publish_cb)
self._dbus_service.register_callback(ON_LOST_FOCUS_CB, self._internal_on_lost_focus_cb)
self._dbus_service.register_callback(ON_GOT_FOCUS_CB, self._internal_on_got_focus_cb)
self._has_focus = False
if activity_id is None:
self._activity_id = sugar.util.unique_id()
else:
self._activity_id = activity_id
self._dbus_service = None
self._initial_service = None
self._activity_object = None
self._shared = False
@ -258,6 +175,20 @@ class Activity(gtk.Window):
raise ValueError("Default type must be a valid string.")
self._default_type = default_type
keybindings.setup_global_keys(self)
self.connect('realize', self.__realize)
self.show()
def __realize(self, window):
if not self._dbus_service:
self._register_service()
def _register_service(self):
self._dbus_service = self._get_new_dbus_service()
self._dbus_service.register_callback(ON_PUBLISH_CB, self._internal_on_publish_cb)
def _cleanup(self):
if self._dbus_service:
del self._dbus_service
@ -269,7 +200,7 @@ class Activity(gtk.Window):
def _get_new_dbus_service(self):
"""Create and return a new dbus service object for this Activity.
Allows subclasses to use their own dbus service object if they choose."""
return ActivityDbusService(self)
return ActivityDbusService(self.window.xid, self)
def default_type(self):
return self._default_type
@ -280,113 +211,20 @@ class Activity(gtk.Window):
self._shared = True
self._dbus_service.ActivityShared()
def shared(self):
def get_shared(self):
return self._shared
def has_focus(self):
"""Return whether or not this Activity is visible to the user."""
return self._has_focus
def connect_to_shell(self, service = None):
"""Called by our controller to tell us to initialize and connect
to the shell."""
self.show()
self._dbus_service.connect_to_shell(service)
def _internal_on_connected_to_shell_cb(self, activity_object, activity_id, service=None):
"""Callback when the dbus service object has connected to the shell."""
self._activity_object = activity_object
self._activity_id = activity_id
self._initial_service = service
if service:
self.set_shared()
self.on_connected_to_shell()
def _internal_on_disconnected_from_shell_cb(self):
"""Callback when the dbus service object has disconnected from the shell."""
self._cleanup()
self.on_disconnected_from_shell()
def _internal_on_reconnected_to_shell_cb(self):
"""Callback when the dbus service object has reconnected to the shell."""
self.on_reconnected_to_shell()
def _internal_on_close_from_user_cb(self):
"""Callback when the dbus service object tells us the user has closed our activity."""
self.shutdown()
self.on_close_from_user()
def _internal_on_publish_cb(self):
"""Callback when the dbus service object tells us the user has closed our activity."""
self.publish()
def _internal_on_lost_focus_cb(self):
"""Callback when the dbus service object tells us we have lost focus."""
self._has_focus = False
self.on_lost_focus()
def _internal_on_got_focus_cb(self):
"""Callback when the dbus service object tells us we have gotten focus."""
self._has_focus = True
self.set_has_changes(False)
self.on_got_focus()
def set_ellipsize_tab(self, ellipsize):
"""Sets this Activity's tab text to be ellipsized or not."""
self._activity_object.set_ellipsize_tab(ellipsize)
def set_tab_text(self, text):
"""Sets this Activity's tab text."""
self._activity_object.set_tab_text(text)
def set_can_close(self, can_close):
"""Sets whether or not this Activity can be closed by the user."""
self._activity_object.set_can_close(can_close)
def set_show_tab_icon(self, show_icon):
"""Sets whether or not an icon is shown in this Activity's tab."""
self._activity_object.set_tab_show_icon(show_icon)
def set_tab_icon(self, pixbuf=None, name=None):
"""Set the Activity's tab icon, either from pixbuf data
or by an icon theme icon name."""
if name:
icon_theme = gtk.icon_theme_get_default()
icon_info = icon_theme.lookup_icon(name, gtk.ICON_SIZE_MENU, 0)
if icon_info:
orig_pixbuf = icon_info.load_icon()
pixbuf = orig_pixbuf.scale_simple(16, 16, gtk.gdk.INTERP_BILINEAR)
if pixbuf:
# Dump the pixel data into an array and shove it through dbus
pixarray = []
pixstr = pixbuf.get_pixels();
for c in pixstr:
pixarray.append(c)
self._activity_object.set_tab_icon(pixarray, \
pixbuf.get_colorspace(), \
pixbuf.get_has_alpha(), \
pixbuf.get_bits_per_sample(), \
pixbuf.get_width(), \
pixbuf.get_height(), \
pixbuf.get_rowstride())
def set_has_changes(self, has_changes):
"""Marks this Activity as having changes. This usually means
that this Activity's tab turns a red color or something else
to notify the user that this Activity needs attention."""
if not self.has_focus() and has_changes:
self._activity_object.set_has_changes(True)
else:
self._activity_object.set_has_changes(False)
def get_id(self):
return self._activity_id
def shutdown(self):
"""Disconnect from the shell and clean up."""
self._dbus_service.shutdown()
#############################################################
# Pure Virtual methods that subclasses may/may not implement
#############################################################
@ -395,29 +233,5 @@ class Activity(gtk.Window):
"""Called to request the activity to publish itself on the network."""
pass
def on_lost_focus(self):
"""Triggered when this Activity loses focus."""
pass
def on_got_focus(self):
"""Triggered when this Activity gets focus."""
pass
def on_disconnected_from_shell(self):
"""Triggered when we disconnect from the shell."""
pass
def on_reconnected_to_shell(self):
"""Triggered when the shell's service comes back."""
pass
def on_connected_to_shell(self):
"""Triggered when this Activity has successfully connected to the shell."""
pass
def on_close_from_user(self):
"""Triggered when this Activity is closed by the user."""
pass
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])

View File

@ -1,18 +0,0 @@
import pygtk
pygtk.require('2.0')
import gtk
from sugar.chat.Chat import Chat
class ChatWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self._chat = None
def set_chat(self, chat):
if self._chat != None:
self.remove(self._chat)
self._chat = chat
self.add(self._chat)
self._chat.show()

19
sugar/keybindings.py Normal file
View File

@ -0,0 +1,19 @@
import gtk
import dbus
# FIXME These should be handled by the wm, but it's incovenient
# to do that with matchbox at the moment
def setup_global_keys(window, shell = None):
if not shell:
bus = dbus.SessionBus()
proxy_obj = bus.get_object('com.redhat.Sugar.Shell', '/com/redhat/Sugar/Shell')
shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.Shell')
window.connect("key-press-event", __key_press_event_cb, shell)
def __key_press_event_cb(window, event, shell):
if event.keyval == gtk.keysyms.F2:
shell.toggle_people()
if event.keyval == gtk.keysyms.F3:
shell.toggle_console()