Merge design-review-3-no-tabs
Conflicts: activities/browser/BrowserActivity.py shell/ActivityContainer.py shell/ActivityHost.py shell/ConsoleLogger.py shell/HomeWindow.py shell/Shell.py shell/WindowManager.py sugar/activity/Activity.py
This commit is contained in:
commit
3e30af13f6
@ -26,13 +26,53 @@ class BrowserActivity(Activity):
|
|||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
Activity.__init__(self, _BROWSER_ACTIVITY_TYPE)
|
Activity.__init__(self, _BROWSER_ACTIVITY_TYPE)
|
||||||
|
|
||||||
self.uri = args[0]
|
if len(args) > 0:
|
||||||
|
self.uri = args[0]
|
||||||
|
else:
|
||||||
|
self.uri = 'http://www.google.com'
|
||||||
|
|
||||||
self._mode = BrowserActivity.SOLO
|
self._mode = BrowserActivity.SOLO
|
||||||
self._share_service = None
|
self._share_service = None
|
||||||
self._model_service = None
|
self._model_service = None
|
||||||
self._notif_service = None
|
self._notif_service = None
|
||||||
self._model = 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):
|
def _service_appeared_cb(self, pservice, buddy, service):
|
||||||
# Make sure the service is for our activity
|
# Make sure the service is for our activity
|
||||||
@ -75,50 +115,6 @@ class BrowserActivity(Activity):
|
|||||||
self._notif_bar.set_icon('stock_shared-by-me')
|
self._notif_bar.set_icon('stock_shared-by-me')
|
||||||
self._notif_bar.show()
|
self._notif_bar.show()
|
||||||
|
|
||||||
def on_connected_to_shell(self):
|
|
||||||
self.set_ellipsize_tab(True)
|
|
||||||
self.set_can_close(True)
|
|
||||||
self.set_tab_text("Web Page")
|
|
||||||
self.set_tab_icon(name="web-browser")
|
|
||||||
self.set_show_tab_icon(True)
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
plug = self.gtk_plug()
|
|
||||||
plug.add(vbox)
|
|
||||||
plug.show()
|
|
||||||
|
|
||||||
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):
|
def get_embed(self):
|
||||||
return self.embed
|
return self.embed
|
||||||
|
|
||||||
@ -139,7 +135,7 @@ class BrowserActivity(Activity):
|
|||||||
self.set_mode(BrowserActivity.LEADING)
|
self.set_mode(BrowserActivity.LEADING)
|
||||||
|
|
||||||
def __title_cb(self, embed):
|
def __title_cb(self, embed):
|
||||||
self.set_tab_text(embed.get_title())
|
self.set_title(embed.get_title())
|
||||||
|
|
||||||
def __shared_location_changed_cb(self, model, key):
|
def __shared_location_changed_cb(self, model, key):
|
||||||
self.set_has_changes(True)
|
self.set_has_changes(True)
|
||||||
|
@ -1,167 +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)
|
|
||||||
|
|
||||||
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()
|
|
||||||
self.notebook.set_scrollable(True)
|
|
||||||
|
|
||||||
tab_label = gtk.Label(_("Everyone"))
|
|
||||||
self._start_page = StartPage(self, 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)
|
|
||||||
|
|
||||||
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()
|
|
||||||
self._chat_window.set_transient_for(self.window)
|
|
||||||
|
|
||||||
self._chat_wm = WindowManager(self._chat_window)
|
|
||||||
self._chat_wm.set_animation(WindowManager.ANIMATION_SLIDE_IN)
|
|
||||||
self._chat_wm.set_type(WindowManager.TYPE_POPUP)
|
|
||||||
self._chat_wm.set_geometry(0.28, 0.1, 0.5, 0.9)
|
|
||||||
self._chat_wm.set_key(gtk.keysyms.F1)
|
|
||||||
|
|
||||||
self._mesh_chat = MeshChat()
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
self.window.show()
|
|
||||||
|
|
||||||
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 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:
|
|
||||||
self.current_activity.lost_focus()
|
|
||||||
|
|
||||||
self.set_current_activity(new_activity)
|
|
||||||
|
|
||||||
if self.current_activity != None:
|
|
||||||
self.current_activity.got_focus()
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,269 +0,0 @@
|
|||||||
import dbus
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
|
|
||||||
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, activity_container, activity_name, default_type, activity_id = None):
|
|
||||||
self.peer_service = None
|
|
||||||
|
|
||||||
self.activity_name = activity_name
|
|
||||||
self.ellipsize_tab = False
|
|
||||||
self._shared = False
|
|
||||||
|
|
||||||
self._signal_helper = ActivityHostSignalHelper(self)
|
|
||||||
|
|
||||||
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._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 __close_button_clicked_reply_cb(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __close_button_clicked_error_cb(self, error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def publish(self):
|
|
||||||
self._activity_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
|
|
||||||
|
|
||||||
def connect(self, signal, func):
|
|
||||||
self._signal_helper.connect(signal, func)
|
|
||||||
|
|
||||||
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", \
|
|
||||||
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")
|
|
||||||
self.activity_container.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", \
|
|
||||||
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 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 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()
|
|
29
shell/ActivityRegistry.py
Normal file
29
shell/ActivityRegistry.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import dbus
|
||||||
|
|
||||||
|
class ActivityInfo:
|
||||||
|
def __init__(self, name, title):
|
||||||
|
self._name = name
|
||||||
|
self._title = title
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def get_title(self):
|
||||||
|
return self._title
|
||||||
|
|
||||||
|
class ActivityRegistry(dbus.service.Object):
|
||||||
|
"""Dbus service that tracks the available activities"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._activities = []
|
||||||
|
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
bus_name = dbus.service.BusName('com.redhat.Sugar.ActivityRegistry', bus = bus)
|
||||||
|
dbus.service.Object.__init__(self, bus_name, '/com/redhat/Sugar/ActivityRegistry')
|
||||||
|
|
||||||
|
@dbus.service.method("com.redhat.Sugar.ActivityRegistry")
|
||||||
|
def add(self, name, title):
|
||||||
|
self._activities.append(ActivityInfo(name, title))
|
||||||
|
|
||||||
|
def list_activities(self):
|
||||||
|
return self._activities
|
@ -1,9 +1,6 @@
|
|||||||
import gtk
|
import gtk
|
||||||
import dbus
|
import dbus
|
||||||
|
|
||||||
from WindowManager import WindowManager
|
|
||||||
from ActivityContainer import ActivityContainer
|
|
||||||
|
|
||||||
class ConsoleLogger(dbus.service.Object):
|
class ConsoleLogger(dbus.service.Object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
session_bus = dbus.SessionBus()
|
session_bus = dbus.SessionBus()
|
||||||
@ -21,9 +18,8 @@ class ConsoleLogger(dbus.service.Object):
|
|||||||
|
|
||||||
self._consoles = {}
|
self._consoles = {}
|
||||||
|
|
||||||
console_wm = WindowManager(self._window)
|
def get_window(self):
|
||||||
console_wm.set_type(WindowManager.TYPE_POPUP)
|
return self._window
|
||||||
console_wm.set_geometry(0.1, 0.1, 0.8, 0.8)
|
|
||||||
|
|
||||||
def _create_console(self, application):
|
def _create_console(self, application):
|
||||||
sw = gtk.ScrolledWindow()
|
sw = gtk.ScrolledWindow()
|
||||||
@ -51,4 +47,3 @@ class ConsoleLogger(dbus.service.Object):
|
|||||||
|
|
||||||
buf = console.get_buffer()
|
buf = console.get_buffer()
|
||||||
buf.insert(buf.get_end_iter(), message)
|
buf.insert(buf.get_end_iter(), message)
|
||||||
|
|
||||||
|
102
shell/HomeWindow.py
Normal file
102
shell/HomeWindow.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
import gtk
|
||||||
|
import wnck
|
||||||
|
|
||||||
|
from sugar.activity import Activity
|
||||||
|
|
||||||
|
class NewActivityButton(gtk.MenuToolButton):
|
||||||
|
def __init__(self, home):
|
||||||
|
gtk.MenuToolButton.__init__(self, None, _('New Activity'))
|
||||||
|
|
||||||
|
self._home = home
|
||||||
|
|
||||||
|
self.set_menu(gtk.Menu())
|
||||||
|
self.connect("show-menu", self.__show_menu_cb)
|
||||||
|
|
||||||
|
def __show_menu_cb(self, button):
|
||||||
|
menu = gtk.Menu()
|
||||||
|
|
||||||
|
for activity_info in self._home.list_activities():
|
||||||
|
item = gtk.MenuItem(activity_info.get_title(), False)
|
||||||
|
name = activity_info.get_name()
|
||||||
|
item.connect('activate', self.__menu_item_activate_cb, name)
|
||||||
|
menu.append(item)
|
||||||
|
item.show()
|
||||||
|
|
||||||
|
self.set_menu(menu)
|
||||||
|
|
||||||
|
def __menu_item_activate_cb(self, item, name):
|
||||||
|
self._home.create(name)
|
||||||
|
|
||||||
|
class Toolbar(gtk.Toolbar):
|
||||||
|
def __init__(self, shell):
|
||||||
|
gtk.Toolbar.__init__(self)
|
||||||
|
|
||||||
|
new_activity_button = NewActivityButton(shell)
|
||||||
|
self.insert(new_activity_button, -1)
|
||||||
|
new_activity_button.show()
|
||||||
|
|
||||||
|
class ActivityGrid(gtk.VBox):
|
||||||
|
def __init__(self, home):
|
||||||
|
gtk.VBox.__init__(self)
|
||||||
|
|
||||||
|
self._home = home
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def _add_all(self):
|
||||||
|
screen = wnck.screen_get_default()
|
||||||
|
for window in screen.get_windows():
|
||||||
|
if not window.is_skip_tasklist():
|
||||||
|
self.add(window)
|
||||||
|
|
||||||
|
def _remove_all(self):
|
||||||
|
for child in self.get_children():
|
||||||
|
self.remove(child)
|
||||||
|
|
||||||
|
def add(self, window):
|
||||||
|
button = gtk.Button(window.get_name())
|
||||||
|
button.connect('clicked', self.__button_clicked_cb, window)
|
||||||
|
self.pack_start(button, False)
|
||||||
|
button.show()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self._remove_all()
|
||||||
|
self._add_all()
|
||||||
|
|
||||||
|
def __button_clicked_cb(self, button, window):
|
||||||
|
self._home.activate(window)
|
||||||
|
|
||||||
|
class HomeWindow(gtk.Window):
|
||||||
|
def __init__(self, shell):
|
||||||
|
gtk.Window.__init__(self)
|
||||||
|
|
||||||
|
self._shell = shell
|
||||||
|
|
||||||
|
vbox = gtk.VBox()
|
||||||
|
|
||||||
|
toolbar = Toolbar(self)
|
||||||
|
vbox.pack_start(toolbar, False)
|
||||||
|
toolbar.show()
|
||||||
|
|
||||||
|
self._grid = ActivityGrid(self)
|
||||||
|
vbox.pack_start(self._grid)
|
||||||
|
self._grid.show()
|
||||||
|
|
||||||
|
self.add(vbox)
|
||||||
|
vbox.show()
|
||||||
|
|
||||||
|
def list_activities(self):
|
||||||
|
return self._shell.get_registry().list_activities()
|
||||||
|
|
||||||
|
def create(self, activity_name):
|
||||||
|
Activity.create(activity_name)
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
def activate(self, activity_window):
|
||||||
|
activity_window.activate(gtk.get_current_event_time())
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
self._grid.update()
|
||||||
|
gtk.Window.show(self)
|
@ -16,11 +16,11 @@ class PresenceWindow(gtk.Window):
|
|||||||
_MODEL_COL_BUDDY = 2
|
_MODEL_COL_BUDDY = 2
|
||||||
_MODEL_COL_VISIBLE = 3
|
_MODEL_COL_VISIBLE = 3
|
||||||
|
|
||||||
def __init__(self, activity_container):
|
def __init__(self, shell):
|
||||||
gtk.Window.__init__(self)
|
gtk.Window.__init__(self)
|
||||||
|
|
||||||
self._activity_container = activity_container
|
|
||||||
self._activity = None
|
self._activity = None
|
||||||
|
self._shell = shell
|
||||||
|
|
||||||
self._pservice = PresenceService.get_instance()
|
self._pservice = PresenceService.get_instance()
|
||||||
self._pservice.connect("buddy-appeared", self._on_buddy_appeared_cb)
|
self._pservice.connect("buddy-appeared", self._on_buddy_appeared_cb)
|
||||||
@ -51,7 +51,6 @@ class PresenceWindow(gtk.Window):
|
|||||||
self._share_button.set_sensitive(False)
|
self._share_button.set_sensitive(False)
|
||||||
else:
|
else:
|
||||||
self._share_button.set_sensitive(True)
|
self._share_button.set_sensitive(True)
|
||||||
self._activity.connect('shared', lambda w: self._share_button.set_sensitive(False))
|
|
||||||
else:
|
else:
|
||||||
self._share_button.set_sensitive(False)
|
self._share_button.set_sensitive(False)
|
||||||
|
|
||||||
@ -113,7 +112,7 @@ class PresenceWindow(gtk.Window):
|
|||||||
vbox.show()
|
vbox.show()
|
||||||
|
|
||||||
def _share_button_clicked_cb(self, button):
|
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):
|
def _on_buddyList_buddy_selected(self, view, *args):
|
||||||
(model, aniter) = view.get_selection().get_selected()
|
(model, aniter) = view.get_selection().get_selected()
|
||||||
|
121
shell/Shell.py
121
shell/Shell.py
@ -1,10 +1,37 @@
|
|||||||
import dbus
|
import dbus
|
||||||
|
import gtk
|
||||||
import gobject
|
import gobject
|
||||||
|
import wnck
|
||||||
|
|
||||||
from sugar.LogWriter import LogWriter
|
from sugar.LogWriter import LogWriter
|
||||||
from WindowManager import WindowManager
|
|
||||||
from ConsoleLogger import ConsoleLogger
|
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
|
||||||
|
from Owner import ShellOwner
|
||||||
|
|
||||||
|
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):
|
class Shell(gobject.GObject):
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
@ -15,24 +42,96 @@ class Shell(gobject.GObject):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
|
self._screen = wnck.screen_get_default()
|
||||||
|
|
||||||
def start(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 = LogWriter("Shell", False)
|
||||||
log_writer.start()
|
log_writer.start()
|
||||||
|
|
||||||
session_bus = dbus.SessionBus()
|
session_bus = dbus.SessionBus()
|
||||||
service = dbus.service.BusName("com.redhat.Sugar.Shell", bus=session_bus)
|
bus_name = dbus.service.BusName('com.redhat.Sugar.Shell', bus=session_bus)
|
||||||
|
ShellDbusService(self, bus_name)
|
||||||
|
|
||||||
activity_container = ActivityContainer(service, session_bus)
|
self._owner = ShellOwner()
|
||||||
activity_container.window.connect('destroy', self.__activity_container_destroy_cb)
|
|
||||||
activity_container.show()
|
|
||||||
|
|
||||||
wm = WindowManager(activity_container.window)
|
self._registry = ActivityRegistry()
|
||||||
wm.show()
|
|
||||||
|
|
||||||
def __activity_container_destroy_cb(self, activity_container):
|
self._home_window = HomeWindow(self)
|
||||||
self.emit('close')
|
keybindings.setup_global_keys(self._home_window, self)
|
||||||
|
self._home_window.show()
|
||||||
|
|
||||||
|
self._presence_window = PresenceWindow(self)
|
||||||
|
self._presence_window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||||
|
self._presence_window.set_skip_taskbar_hint(True)
|
||||||
|
self._presence_window.set_decorated(False)
|
||||||
|
keybindings.setup_global_keys(self._presence_window, self)
|
||||||
|
|
||||||
|
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_activity_from_xid(self, xid):
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
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')
|
||||||
|
|
||||||
|
def get_current_activity(self):
|
||||||
|
window = self._screen.get_active_window()
|
||||||
|
|
||||||
|
if window and window.is_skip_tasklist():
|
||||||
|
window = self._screen.get_previously_active_window()
|
||||||
|
|
||||||
|
if window and not window.is_skip_tasklist():
|
||||||
|
return self.get_activity_from_xid(window.get_xid())
|
||||||
|
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_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||||
|
window.set_skip_taskbar_hint(True)
|
||||||
|
window.set_decorated(False)
|
||||||
|
keybindings.setup_global_keys(window, self)
|
||||||
|
chat = ActivityChat(activity)
|
||||||
|
window.add(chat)
|
||||||
|
chat.show()
|
||||||
|
self._chat_windows[activity_id] = window
|
||||||
|
else:
|
||||||
|
window = self._chat_windows[activity_id]
|
||||||
|
|
||||||
|
window.move(210, 10)
|
||||||
|
window.resize(380, 440)
|
||||||
|
self._toggle_window_visibility(window)
|
||||||
|
|
||||||
|
self._presence_window.move(10, 10)
|
||||||
|
self._presence_window.resize(180, 440)
|
||||||
|
self._presence_window.set_activity(activity)
|
||||||
|
self._toggle_window_visibility(self._presence_window)
|
||||||
|
|
||||||
|
def toggle_console(self):
|
||||||
|
self._toggle_window_visibility(self._console.get_window())
|
||||||
|
|
||||||
|
def get_registry(self):
|
||||||
|
return self._registry
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
shell = Shell()
|
shell = Shell()
|
||||||
|
@ -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('"', '"')
|
|
||||||
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_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
|
|
@ -1,206 +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_ACTIVITY = 0
|
|
||||||
TYPE_POPUP = 1
|
|
||||||
|
|
||||||
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_ACTIVITY:
|
|
||||||
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_ACTIVITY:
|
|
||||||
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:
|
|
||||||
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)
|
|
@ -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
|
|
@ -1,6 +0,0 @@
|
|||||||
googledir = $(pkgdatadir)/shell/google
|
|
||||||
google_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
google.py \
|
|
||||||
GoogleSOAPFacade.py \
|
|
||||||
SOAP.py
|
|
3974
shell/google/SOAP.py
3974
shell/google/SOAP.py
File diff suppressed because it is too large
Load Diff
@ -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:])
|
|
574
shell/shell.py
574
shell/shell.py
@ -1,574 +0,0 @@
|
|||||||
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.ActivityChat import ActivityChat
|
|
||||||
from sugar.chat.MeshChat import MeshChat
|
|
||||||
from sugar.LogWriter import LogWriter
|
|
||||||
|
|
||||||
from Owner import ShellOwner
|
|
||||||
from StartPage import StartPage
|
|
||||||
from WindowManager import WindowManager
|
|
||||||
from PresenceWindow import PresenceWindow
|
|
||||||
|
|
||||||
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, activity_container, activity_name, default_type, activity_id = None):
|
|
||||||
self.peer_service = None
|
|
||||||
|
|
||||||
self.activity_name = activity_name
|
|
||||||
self.ellipsize_tab = False
|
|
||||||
self._shared = False
|
|
||||||
|
|
||||||
self._signal_helper = ActivityHostSignalHelper(self)
|
|
||||||
|
|
||||||
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._activity_chat = ActivityChat(self)
|
|
||||||
|
|
||||||
def got_focus(self):
|
|
||||||
if self.peer_service != None:
|
|
||||||
self.peer_service.got_focus()
|
|
||||||
|
|
||||||
def lost_focus(self):
|
|
||||||
if self.peer_service != None:
|
|
||||||
self.peer_service.lost_focus()
|
|
||||||
|
|
||||||
def get_chat(self):
|
|
||||||
return self._activity_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._activity_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
|
|
||||||
|
|
||||||
def connect(self, signal, func):
|
|
||||||
self._signal_helper.connect(signal, func)
|
|
||||||
|
|
||||||
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", \
|
|
||||||
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")
|
|
||||||
self.activity_container.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", \
|
|
||||||
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 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 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()
|
|
||||||
self.notebook.set_scrollable(True)
|
|
||||||
|
|
||||||
tab_label = gtk.Label("Everyone")
|
|
||||||
self._start_page = StartPage(self, 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(170, WindowManager.ABSOLUTE)
|
|
||||||
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(420, WindowManager.ABSOLUTE)
|
|
||||||
self._chat_wm.set_height(380, WindowManager.ABSOLUTE)
|
|
||||||
self._chat_wm.set_position(WindowManager.TOP)
|
|
||||||
self._chat_wm.manage()
|
|
||||||
|
|
||||||
self._mesh_chat = MeshChat()
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
self.window.show()
|
|
||||||
|
|
||||||
def set_current_activity(self, activity):
|
|
||||||
if self.current_activity != None:
|
|
||||||
self.current_activity.lost_focus()
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
# For some reason the substitution screw up window position
|
|
||||||
self._chat_wm.update()
|
|
||||||
|
|
||||||
if self.current_activity != None:
|
|
||||||
self.current_activity.got_focus()
|
|
||||||
|
|
||||||
def notebook_tab_changed(self, notebook, page, page_number):
|
|
||||||
new_activity = notebook.get_nth_page(page_number).get_data("sugar-activity")
|
|
||||||
self.set_current_activity(new_activity)
|
|
||||||
|
|
||||||
def switch_to_activity(self, activity_id):
|
|
||||||
found = False
|
|
||||||
for owner, activity in self.activities:
|
|
||||||
if activity.get_host_activity_id() == activity_id:
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Find the activity in the notebook
|
|
||||||
activity_page = None
|
|
||||||
npages = self.notebook.get_n_pages()
|
|
||||||
for pageno in range(1, npages):
|
|
||||||
activity = self.notebook.get_nth_page(pageno).get_data("sugar-activity")
|
|
||||||
if activity and activity.get_host_activity_id() == activity_id:
|
|
||||||
activity_page = pageno
|
|
||||||
break
|
|
||||||
if not activity_page:
|
|
||||||
return
|
|
||||||
|
|
||||||
print "switching to activity page %d" % activity_page
|
|
||||||
self.notebook.set_current_page(activity_page)
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
def have_activity(self, activity_id):
|
|
||||||
for owner, activity in self.activities:
|
|
||||||
list_activity_id = activity.get_host_activity_id()
|
|
||||||
if activity_id == list_activity_id:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@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_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
|
|
||||||
self._window.set_decorated(False)
|
|
||||||
self._window.set_skip_taskbar_hint(True)
|
|
||||||
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 = {}
|
|
||||||
|
|
||||||
console_wm = WindowManager(self._window)
|
|
||||||
|
|
||||||
console_wm.set_width(0.7, WindowManager.SCREEN_RELATIVE)
|
|
||||||
console_wm.set_height(0.9, WindowManager.SCREEN_RELATIVE)
|
|
||||||
console_wm.set_position(WindowManager.BOTTOM)
|
|
||||||
console_wm.manage()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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...'
|
|
26
shell/sugar
26
shell/sugar
@ -9,6 +9,9 @@ import pygtk
|
|||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gobject
|
import gobject
|
||||||
|
|
||||||
|
# FIXME How to pick a good display number
|
||||||
|
XEPHYR_DISPLAY = 100
|
||||||
|
|
||||||
def add_to_python_path(path):
|
def add_to_python_path(path):
|
||||||
sys.path.insert(0, path)
|
sys.path.insert(0, path)
|
||||||
if os.environ.has_key('PYTHONPATH'):
|
if os.environ.has_key('PYTHONPATH'):
|
||||||
@ -23,7 +26,6 @@ def start_dbus():
|
|||||||
dbus_file = os.fdopen(dbus_stdout)
|
dbus_file = os.fdopen(dbus_stdout)
|
||||||
addr = dbus_file.readline()
|
addr = dbus_file.readline()
|
||||||
addr = addr.strip()
|
addr = addr.strip()
|
||||||
print "dbus-daemon pid is %d, session bus address is %s" % (dbus_pid, addr)
|
|
||||||
dbus_file.close()
|
dbus_file.close()
|
||||||
|
|
||||||
os.environ["DBUS_SESSION_BUS_ADDRESS"] = addr
|
os.environ["DBUS_SESSION_BUS_ADDRESS"] = addr
|
||||||
@ -37,6 +39,20 @@ def stop_dbus(dbus_pid):
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
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
|
i = 0
|
||||||
dbus_daemon_pid = None
|
dbus_daemon_pid = None
|
||||||
for arg in sys.argv:
|
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_NICK_NAME'] = nick
|
||||||
os.environ['SUGAR_USER_DIR'] = os.path.expanduser('~/.sugar')
|
os.environ['SUGAR_USER_DIR'] = os.path.expanduser('~/.sugar')
|
||||||
|
|
||||||
|
|
||||||
curdir = os.path.abspath(os.path.dirname(__file__))
|
curdir = os.path.abspath(os.path.dirname(__file__))
|
||||||
basedir = os.path.dirname(curdir)
|
basedir = os.path.dirname(curdir)
|
||||||
|
|
||||||
@ -68,6 +83,10 @@ else:
|
|||||||
import sugar.env
|
import sugar.env
|
||||||
add_to_python_path(os.path.join(sugar.env.get_data_dir(), 'shell'))
|
add_to_python_path(os.path.join(sugar.env.get_data_dir(), 'shell'))
|
||||||
print 'Running the installed sugar...'
|
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.'
|
print 'Redirecting output to the console, press Ctrl+Down to open it.'
|
||||||
|
|
||||||
@ -78,3 +97,6 @@ session.start()
|
|||||||
|
|
||||||
if dbus_daemon_pid:
|
if dbus_daemon_pid:
|
||||||
stop_dbus(dbus_daemon_pid)
|
stop_dbus(dbus_daemon_pid)
|
||||||
|
|
||||||
|
stop_matchbox()
|
||||||
|
stop_xephyr()
|
||||||
|
@ -9,6 +9,8 @@ pygtk.require('2.0')
|
|||||||
import gtk, gobject
|
import gtk, gobject
|
||||||
|
|
||||||
from sugar.LogWriter import LogWriter
|
from sugar.LogWriter import LogWriter
|
||||||
|
from sugar import keybindings
|
||||||
|
import sugar.util
|
||||||
|
|
||||||
SHELL_SERVICE_NAME = "com.redhat.Sugar.Shell"
|
SHELL_SERVICE_NAME = "com.redhat.Sugar.Shell"
|
||||||
SHELL_SERVICE_PATH = "/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_NAME = "com.redhat.Sugar.Activity"
|
||||||
ACTIVITY_SERVICE_PATH = "/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"
|
ON_PUBLISH_CB = "publish"
|
||||||
|
|
||||||
def get_path(activity_name):
|
def get_path(activity_name):
|
||||||
@ -65,14 +61,10 @@ class ActivityFactory(dbus.service.Object):
|
|||||||
service = Service.deserialize(serialized_service)
|
service = Service.deserialize(serialized_service)
|
||||||
|
|
||||||
activity = self._class(args)
|
activity = self._class(args)
|
||||||
gobject.idle_add(self._start_activity_cb, activity, service)
|
|
||||||
|
|
||||||
@dbus.service.method("com.redhat.Sugar.ActivityFactory")
|
@dbus.service.method("com.redhat.Sugar.ActivityFactory")
|
||||||
def create(self, args):
|
def create(self):
|
||||||
self.create_with_service(None, args)
|
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):
|
def create(activity_name, service = None, args = None):
|
||||||
"""Create a new activity from his name."""
|
"""Create a new activity from his name."""
|
||||||
@ -84,11 +76,21 @@ def create(activity_name, service = None, args = None):
|
|||||||
proxy_obj = bus.get_object(factory_name, factory_path)
|
proxy_obj = bus.get_object(factory_name, factory_path)
|
||||||
factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory")
|
factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory")
|
||||||
|
|
||||||
if service:
|
if service and args:
|
||||||
serialized_service = service.serialize(service)
|
serialized_service = service.serialize(service)
|
||||||
factory.create_with_service(serialized_service, args)
|
factory.create_with_service(serialized_service, args)
|
||||||
else:
|
else:
|
||||||
factory.create(args)
|
factory.create()
|
||||||
|
|
||||||
|
def _get_registry():
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
proxy_obj = bus.get_object("com.redhat.Sugar.ActivityRegistry",
|
||||||
|
"/com/redhat/Sugar/ActivityRegistry")
|
||||||
|
return dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityRegistry")
|
||||||
|
|
||||||
|
def list_activities():
|
||||||
|
registry = _get_registry()
|
||||||
|
return registry.list_activities()
|
||||||
|
|
||||||
def main(activity_name, activity_class):
|
def main(activity_name, activity_class):
|
||||||
"""Starts the activity main loop."""
|
"""Starts the activity main loop."""
|
||||||
@ -97,6 +99,9 @@ def main(activity_name, activity_class):
|
|||||||
|
|
||||||
factory = ActivityFactory(activity_name, activity_class)
|
factory = ActivityFactory(activity_name, activity_class)
|
||||||
|
|
||||||
|
registry = _get_registry()
|
||||||
|
registry.add(activity_name, activity_name)
|
||||||
|
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
|
||||||
class ActivityDbusService(dbus.service.Object):
|
class ActivityDbusService(dbus.service.Object):
|
||||||
@ -105,29 +110,19 @@ class ActivityDbusService(dbus.service.Object):
|
|||||||
The dbus service is separate from the actual Activity object so that we can
|
The dbus service is separate from the actual Activity object so that we can
|
||||||
tightly control what stuff passes through the dbus python bindings."""
|
tightly control what stuff passes through the dbus python bindings."""
|
||||||
|
|
||||||
_ALLOWED_CALLBACKS = [ON_CONNECTED_TO_SHELL_CB, ON_DISCONNECTED_FROM_SHELL_CB, \
|
_ALLOWED_CALLBACKS = [ON_PUBLISH_CB]
|
||||||
ON_RECONNECTED_TO_SHELL_CB, ON_CLOSE_FROM_USER_CB, ON_LOST_FOCUS_CB, \
|
|
||||||
ON_GOT_FOCUS_CB, ON_PUBLISH_CB]
|
|
||||||
|
|
||||||
def __init__(self, activity):
|
def __init__(self, xid, activity):
|
||||||
self._activity = 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 = {}
|
self._callbacks = {}
|
||||||
for cb in self._ALLOWED_CALLBACKS:
|
for cb in self._ALLOWED_CALLBACKS:
|
||||||
self._callbacks[cb] = None
|
self._callbacks[cb] = None
|
||||||
|
|
||||||
def __del__(self):
|
bus = dbus.SessionBus()
|
||||||
if self._activity_id:
|
service_name = ACTIVITY_SERVICE_NAME + "%s" % xid
|
||||||
del self._service
|
object_path = ACTIVITY_SERVICE_PATH + "/%s" % xid
|
||||||
del self._activity_container
|
service = dbus.service.BusName(service_name, bus=bus)
|
||||||
del self._activity_conainer_object
|
dbus.service.Object.__init__(self, service, object_path)
|
||||||
del self._activity_object
|
|
||||||
self._bus.remove_signal_receiver(self.name_owner_changed, dbus_interface="org.freedesktop.DBus", signal_name="NameOwnerChanged")
|
|
||||||
del self._bus
|
|
||||||
|
|
||||||
def register_callback(self, name, callback):
|
def register_callback(self, name, callback):
|
||||||
if name not in self._ALLOWED_CALLBACKS:
|
if name not in self._ALLOWED_CALLBACKS:
|
||||||
@ -146,97 +141,33 @@ class ActivityDbusService(dbus.service.Object):
|
|||||||
if name in self._ALLOWED_CALLBACKS and self._callbacks[name]:
|
if name in self._ALLOWED_CALLBACKS and self._callbacks[name]:
|
||||||
gobject.idle_add(self._call_callback_cb, self._callbacks[name], *args)
|
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)
|
@dbus.service.method(ACTIVITY_SERVICE_NAME)
|
||||||
def publish(self):
|
def publish(self):
|
||||||
"""Called by the shell to request the activity to publish itself on the network."""
|
"""Called by the shell to request the activity to publish itself on the network."""
|
||||||
self._call_callback(ON_PUBLISH_CB)
|
self._call_callback(ON_PUBLISH_CB)
|
||||||
|
|
||||||
@dbus.service.signal(ACTIVITY_SERVICE_NAME)
|
@dbus.service.method(ACTIVITY_SERVICE_NAME)
|
||||||
def ActivityShared(self):
|
def get_id(self):
|
||||||
pass
|
"""Get the activity identifier"""
|
||||||
|
self._activity.get_id()
|
||||||
|
|
||||||
class Activity(object):
|
@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."""
|
"""Base Activity class that all other Activities derive from."""
|
||||||
|
|
||||||
def __init__(self, default_type):
|
def __init__(self, default_type, activity_id = None):
|
||||||
self._dbus_service = self._get_new_dbus_service()
|
gtk.Window.__init__(self)
|
||||||
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)
|
if activity_id is None:
|
||||||
self._dbus_service.register_callback(ON_RECONNECTED_TO_SHELL_CB, self._internal_on_reconnected_to_shell_cb)
|
self._activity_id = sugar.util.unique_id()
|
||||||
self._dbus_service.register_callback(ON_CLOSE_FROM_USER_CB, self._internal_on_close_from_user_cb)
|
else:
|
||||||
self._dbus_service.register_callback(ON_PUBLISH_CB, self._internal_on_publish_cb)
|
self._activity_id = activity_id
|
||||||
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._dbus_service = None
|
||||||
self._has_focus = False
|
|
||||||
self._plug = None
|
|
||||||
self._initial_service = None
|
self._initial_service = None
|
||||||
self._activity_object = None
|
self._activity_object = None
|
||||||
self._shared = False
|
self._shared = False
|
||||||
@ -244,10 +175,21 @@ class Activity(object):
|
|||||||
raise ValueError("Default type must be a valid string.")
|
raise ValueError("Default type must be a valid string.")
|
||||||
self._default_type = default_type
|
self._default_type = default_type
|
||||||
|
|
||||||
|
keybindings.setup_global_keys(self)
|
||||||
|
|
||||||
|
self.connect('realize', self.__realize)
|
||||||
|
|
||||||
|
self.present()
|
||||||
|
|
||||||
|
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):
|
def _cleanup(self):
|
||||||
if self._plug:
|
|
||||||
self._plug.destroy()
|
|
||||||
self._plug = None
|
|
||||||
if self._dbus_service:
|
if self._dbus_service:
|
||||||
del self._dbus_service
|
del self._dbus_service
|
||||||
self._dbus_service = None
|
self._dbus_service = None
|
||||||
@ -258,7 +200,7 @@ class Activity(object):
|
|||||||
def _get_new_dbus_service(self):
|
def _get_new_dbus_service(self):
|
||||||
"""Create and return a new dbus service object for this Activity.
|
"""Create and return a new dbus service object for this Activity.
|
||||||
Allows subclasses to use their own dbus service object if they choose."""
|
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):
|
def default_type(self):
|
||||||
return self._default_type
|
return self._default_type
|
||||||
@ -269,119 +211,20 @@ class Activity(object):
|
|||||||
self._shared = True
|
self._shared = True
|
||||||
self._dbus_service.ActivityShared()
|
self._dbus_service.ActivityShared()
|
||||||
|
|
||||||
def shared(self):
|
def get_shared(self):
|
||||||
return self._shared
|
return self._shared
|
||||||
|
|
||||||
def has_focus(self):
|
def has_focus(self):
|
||||||
"""Return whether or not this Activity is visible to the user."""
|
"""Return whether or not this Activity is visible to the user."""
|
||||||
return self._has_focus
|
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._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._window_id = self._activity_object.get_host_xembed_id()
|
|
||||||
print "Activity: XEMBED window ID is %s" % self._window_id
|
|
||||||
self._plug = gtk.Plug(self._window_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):
|
def _internal_on_publish_cb(self):
|
||||||
"""Callback when the dbus service object tells us the user has closed our activity."""
|
"""Callback when the dbus service object tells us the user has closed our activity."""
|
||||||
self.publish()
|
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 gtk_plug(self):
|
|
||||||
"""Return our GtkPlug widget."""
|
|
||||||
return self._plug
|
|
||||||
|
|
||||||
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):
|
def get_id(self):
|
||||||
return self._activity_id
|
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
|
# Pure Virtual methods that subclasses may/may not implement
|
||||||
#############################################################
|
#############################################################
|
||||||
@ -390,29 +233,5 @@ class Activity(object):
|
|||||||
"""Called to request the activity to publish itself on the network."""
|
"""Called to request the activity to publish itself on the network."""
|
||||||
pass
|
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__":
|
if __name__ == "__main__":
|
||||||
main(sys.argv[1], sys.argv[2])
|
main(sys.argv[1], sys.argv[2])
|
||||||
|
@ -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()
|
|
21
sugar/keybindings.py
Normal file
21
sugar/keybindings.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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.F1:
|
||||||
|
shell.toggle_home()
|
||||||
|
if event.keyval == gtk.keysyms.F2:
|
||||||
|
shell.toggle_people()
|
||||||
|
if event.keyval == gtk.keysyms.F3:
|
||||||
|
shell.toggle_console()
|
Loading…
Reference in New Issue
Block a user