Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xwebactivity.py460
1 files changed, 460 insertions, 0 deletions
diff --git a/webactivity.py b/webactivity.py
new file mode 100755
index 0000000..2d2a602
--- /dev/null
+++ b/webactivity.py
@@ -0,0 +1,460 @@
+# Copyright (C) 2006, Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+import logging
+from gettext import gettext as _
+
+import gobject
+gobject.threads_init()
+
+import gtk
+import sha
+import base64
+import time
+import shutil
+
+from sugar.activity import activity
+from sugar.graphics import style
+import telepathy
+import telepathy.client
+from sugar.presence import presenceservice
+from sugar.graphics.tray import HTray
+from sugar import profile
+from sugar.graphics.alert import Alert
+from sugar.graphics.icon import Icon
+from sugar import mime
+
+PROFILE_VERSION = 1
+
+_profile_version = 0
+_profile_path = os.path.join(activity.get_activity_root(), 'data/gecko')
+_version_file = os.path.join(_profile_path, 'version')
+
+if os.path.exists(_version_file):
+ f = open(_version_file)
+ _profile_version = int(f.read())
+ f.close()
+
+if _profile_version < PROFILE_VERSION:
+ if not os.path.exists(_profile_path):
+ os.mkdir(_profile_path)
+
+ shutil.copy('cert8.db', _profile_path)
+ os.chmod(os.path.join(_profile_path, 'cert8.db'), 0660)
+
+ f = open(_version_file, 'w')
+ f.write(str(PROFILE_VERSION))
+ f.close()
+
+import hulahop
+hulahop.startup(_profile_path)
+
+from browser import Browser
+from webtoolbar import WebToolbar
+from viewtoolbar import ViewToolbar
+import downloadmanager
+import sessionhistory
+import progresslistener
+
+_LIBRARY_PATH = '/usr/share/library-common/index.html'
+
+from model import Model
+from sugar.presence.tubeconn import TubeConnection
+from messenger import Messenger
+from linkbutton import LinkButton
+
+SERVICE = "org.laptop.WebActivity"
+IFACE = SERVICE
+PATH = "/org/laptop/WebActivity"
+
+_TOOLBAR_BROWSE = 1
+
+_logger = logging.getLogger('web-activity')
+
+class WebActivity(activity.Activity):
+ def __init__(self, handle):
+ activity.Activity.__init__(self, handle)
+
+ _logger.debug('Starting the web activity')
+
+ self._browser = Browser()
+
+ temp_path = os.path.join(self.get_activity_root(), 'instance')
+ downloadmanager.init(self._browser, self, temp_path)
+ sessionhistory.init(self._browser)
+ progresslistener.init(self._browser)
+
+ toolbox = activity.ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+
+ self.toolbar = WebToolbar(self._browser)
+ toolbox.add_toolbar(_('Browse'), self.toolbar)
+ self.toolbar.show()
+
+ self._tray = HTray()
+ self.set_tray(self._tray, gtk.POS_BOTTOM)
+ self._tray.show()
+
+ self.viewtoolbar = ViewToolbar(self)
+ toolbox.add_toolbar(_('View'), self.viewtoolbar)
+ self.viewtoolbar.show()
+
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ self.set_canvas(self._browser)
+ self._browser.show()
+
+ self.session_history = sessionhistory.get_instance()
+ self.session_history.connect('session-link-changed', self._session_history_changed_cb)
+ self.toolbar.connect('add-link', self._link_add_button_cb)
+
+ self._browser.connect("notify::title", self._title_changed_cb)
+
+ self.model = Model()
+ self.model.connect('add_link', self._add_link_model_cb)
+
+ self.current = _('blank')
+ self.webtitle = _('blank')
+ self.connect('key-press-event', self._key_press_cb)
+
+ self.toolbox.set_current_toolbar(_TOOLBAR_BROWSE)
+
+ if handle.uri:
+ self._browser.load_uri(handle.uri)
+ elif not self._jobject.file_path:
+ # TODO: we need this hack until we extend the activity API for
+ # opening URIs and default docs.
+ self._load_homepage()
+
+ self.messenger = None
+ self.connect('shared', self._shared_cb)
+
+ # Get the Presence Service
+ self.pservice = presenceservice.get_instance()
+ try:
+ name, path = self.pservice.get_preferred_connection()
+ self.tp_conn_name = name
+ self.tp_conn_path = path
+ self.conn = telepathy.client.Connection(name, path)
+ except TypeError:
+ _logger.debug('Offline')
+ self.initiating = None
+
+ if self._shared_activity is not None:
+ _logger.debug('shared: %s' %self._shared_activity.props.joined)
+
+ if self._shared_activity is not None:
+ # We are joining the activity
+ _logger.debug('Joined activity')
+ self.connect('joined', self._joined_cb)
+ if self.get_shared():
+ # We've already joined
+ self._joined_cb()
+ else:
+ _logger.debug('Created activity')
+
+ def _shared_cb(self, activity):
+ _logger.debug('My activity was shared')
+ self.initiating = True
+ self._setup()
+
+ _logger.debug('This is my activity: making a tube...')
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
+ SERVICE, {})
+
+ def _setup(self):
+ if self._shared_activity is None:
+ _logger.debug('Failed to share or join activity')
+ return
+
+ bus_name, conn_path, channel_paths = self._shared_activity.get_channels()
+
+ # Work out what our room is called and whether we have Tubes already
+ room = None
+ tubes_chan = None
+ text_chan = None
+ for channel_path in channel_paths:
+ channel = telepathy.client.Channel(bus_name, channel_path)
+ htype, handle = channel.GetHandle()
+ if htype == telepathy.HANDLE_TYPE_ROOM:
+ _logger.debug('Found our room: it has handle#%d "%s"'
+ %(handle, self.conn.InspectHandles(htype, [handle])[0]))
+ room = handle
+ ctype = channel.GetChannelType()
+ if ctype == telepathy.CHANNEL_TYPE_TUBES:
+ _logger.debug('Found our Tubes channel at %s'%channel_path)
+ tubes_chan = channel
+ elif ctype == telepathy.CHANNEL_TYPE_TEXT:
+ _logger.debug('Found our Text channel at %s'%channel_path)
+ text_chan = channel
+
+ if room is None:
+ _logger.debug("Presence service didn't create a room")
+ return
+ if text_chan is None:
+ _logger.debug("Presence service didn't create a text channel")
+ return
+
+ # Make sure we have a Tubes channel - PS doesn't yet provide one
+ if tubes_chan is None:
+ _logger.debug("Didn't find our Tubes channel, requesting one...")
+ tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES,
+ telepathy.HANDLE_TYPE_ROOM, room, True)
+
+ self.tubes_chan = tubes_chan
+ self.text_chan = text_chan
+
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube',
+ self._new_tube_cb)
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _list_tubes_error_cb(self, e):
+ _logger.debug('ListTubes() failed: %s'%e)
+
+ def _joined_cb(self, activity):
+ if not self._shared_activity:
+ return
+
+ _logger.debug('Joined an existing shared activity')
+
+ self.initiating = False
+ self._setup()
+
+ _logger.debug('This is not my activity: waiting for a tube...')
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+
+ def _new_tube_cb(self, id, initiator, type, service, params, state):
+ _logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d' %(id, initiator, type, service,
+ params, state))
+
+ if (type == telepathy.TUBE_TYPE_DBUS and
+ service == SERVICE):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
+
+ self.tube_conn = TubeConnection(self.conn,
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES],
+ id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])
+
+ _logger.debug('Tube created')
+ self.messenger = Messenger(self.tube_conn, self.initiating, self.model)
+
+
+ def _load_homepage(self):
+ if os.path.isfile(_LIBRARY_PATH):
+ self._browser.load_uri('file://' + _LIBRARY_PATH)
+ else:
+ self._browser.load_uri('about:blank')
+
+ def _session_history_changed_cb(self, session_history, link):
+ _logger.debug('NewPage: %s.' %link)
+ self.current = link
+
+ def _title_changed_cb(self, embed, pspec):
+ if embed.props.title is not '':
+ _logger.debug('Title changed=%s' % embed.props.title)
+ self.webtitle = embed.props.title
+
+ def _get_data_from_file_path(self, file_path):
+ fd = open(file_path, 'r')
+ try:
+ data = fd.read()
+ finally:
+ fd.close()
+ return data
+
+ def read_file(self, file_path):
+ if self.metadata['mime_type'] == 'text/plain':
+ data = self._get_data_from_file_path(file_path)
+ self.model.deserialize(data)
+
+ for link in self.model.data['shared_links']:
+ _logger.debug('read: url=%s title=%s d=%s' % (link['url'],
+ link['title'],
+ link['color']))
+ self._add_link_totray(link['url'],
+ base64.b64decode(link['thumb']),
+ link['color'], link['title'],
+ link['owner'], -1, link['hash'])
+ self._browser.set_session(self.model.data['history'])
+ elif self.metadata['mime_type'] == 'text/uri-list':
+ data = self._get_data_from_file_path(file_path)
+ uris = mime.split_uri_list(data)
+ if len(uris) == 1:
+ self._browser.load_uri(uris[0])
+ else:
+ _logger.error('Open uri-list: Does not support'
+ 'list of multiple uris by now.')
+ else:
+ self._browser.load_uri(file_path)
+
+ def write_file(self, file_path):
+ if not self.metadata['mime_type']:
+ self.metadata['mime_type'] = 'text/plain'
+
+ if self.metadata['mime_type'] == 'text/plain':
+ if not self._jobject.metadata['title_set_by_user'] == '1':
+ if self._browser.props.title:
+ self.metadata['title'] = self._browser.props.title
+
+ self.model.data['history'] = self._browser.get_session()
+
+ f = open(file_path, 'w')
+ try:
+ f.write(self.model.serialize())
+ finally:
+ f.close()
+
+ def _link_add_button_cb(self, button):
+ _logger.debug('button: Add link: %s.' % self.current)
+ self._add_link()
+
+ def _key_press_cb(self, widget, event):
+ if event.state & gtk.gdk.CONTROL_MASK:
+ if gtk.gdk.keyval_name(event.keyval) == "l":
+ _logger.debug('keyboard: Add link: %s.' % self.current)
+ self._add_link()
+ return True
+ elif gtk.gdk.keyval_name(event.keyval) == "u":
+ _logger.debug('keyboard: Show source of the current page')
+ self._show_source()
+ return True
+ elif gtk.gdk.keyval_name(event.keyval) == "minus":
+ _logger.debug('keyboard: Zoom out')
+ self._browser.zoom_out()
+ return True
+ elif gtk.gdk.keyval_name(event.keyval) == "plus" \
+ or gtk.gdk.keyval_name(event.keyval) == "equal" :
+ _logger.debug('keyboard: Zoom in')
+ self._browser.zoom_in()
+ return True
+ elif gtk.gdk.keyval_name(event.keyval) == "XF86Start":
+ _logger.debug('keyboard: Show source of the current page SHOW_KEY')
+ self._show_source()
+ return True
+ return False
+
+ def _add_link(self):
+ ''' take screenshot and add link info to the model '''
+ for link in self.model.data['shared_links']:
+ if link['hash'] == sha.new(self.current).hexdigest():
+ _logger.debug('_add_link: link exist already a=%s b=%s' %(
+ link['hash'], sha.new(self.current).hexdigest()))
+ return
+ buffer = self._get_screenshot()
+ timestamp = time.time()
+ self.model.add_link(self.current, self.webtitle, buffer,
+ profile.get_nick_name(),
+ profile.get_color().to_string(), timestamp)
+
+ if self.messenger is not None:
+ self.messenger._add_link(self.current, self.webtitle,
+ profile.get_color().to_string(),
+ profile.get_nick_name(),
+ base64.b64encode(buffer), timestamp)
+
+ def _add_link_model_cb(self, model, index):
+ ''' receive index of new link from the model '''
+ link = self.model.data['shared_links'][index]
+ self._add_link_totray(link['url'], base64.b64decode(link['thumb']),
+ link['color'], link['title'],
+ link['owner'], index, link['hash'])
+
+ def _add_link_totray(self, url, buffer, color, title, owner, index, hash):
+ ''' add a link to the tray '''
+ item = LinkButton(url, buffer, color, title, owner, index, hash)
+ item.connect('clicked', self._link_clicked_cb, url)
+ item.connect('remove_link', self._link_removed_cb)
+ self._tray.add_item(item, index) # use index to add to the tray
+ item.show()
+ if self._tray.props.visible is False:
+ self._tray.show()
+ self.viewtoolbar.traybutton.props.sensitive = True
+
+ def _link_removed_cb(self, button, hash):
+ ''' remove a link from tray and delete it in the model '''
+ self.model.remove_link(hash)
+ self._tray.remove_item(button)
+ if len(self._tray.get_children()) == 0:
+ self.viewtoolbar.traybutton.props.sensitive = False
+
+ def _link_clicked_cb(self, button, url):
+ ''' an item of the link tray has been clicked '''
+ self._browser.load_uri(url)
+
+ def _show_source(self):
+ self._browser.get_source()
+
+ def _pixbuf_save_cb(self, buf, data):
+ data[0] += buf
+ return True
+
+ def get_buffer(self, pixbuf):
+ data = [""]
+ pixbuf.save_to_callback(self._pixbuf_save_cb, "png", {}, data)
+ return str(data[0])
+
+ def _get_screenshot(self):
+ window = self._browser.window
+ width, height = window.get_size()
+
+ screenshot = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, has_alpha=False,
+ bits_per_sample=8, width=width,
+ height=height)
+ screenshot.get_from_drawable(window, window.get_colormap(), 0, 0, 0, 0,
+ width, height)
+
+ screenshot = screenshot.scale_simple(style.zoom(100),
+ style.zoom(80),
+ gtk.gdk.INTERP_BILINEAR)
+
+ buffer = self.get_buffer(screenshot)
+ return buffer
+
+ def can_close(self):
+ if downloadmanager.can_quit():
+ return True
+ else:
+ alert = Alert()
+ alert.props.title = _('Download in progress')
+ alert.props.msg = _('Stopping now will cancel your download')
+ cancel_icon = Icon(icon_name='dialog-cancel')
+ alert.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), cancel_icon)
+ stop_icon = Icon(icon_name='dialog-ok')
+ alert.add_button(gtk.RESPONSE_OK, _('Stop'), stop_icon)
+ stop_icon.show()
+ self.add_alert(alert)
+ alert.connect('response', self.__inprogress_response_cb)
+ alert.show()
+ self.present()
+
+ def __inprogress_response_cb(self, alert, response_id):
+ self.remove_alert(alert)
+ if response_id is gtk.RESPONSE_CANCEL:
+ logging.debug('Keep on')
+ elif response_id == gtk.RESPONSE_OK:
+ logging.debug('Stop downloads and quit')
+ downloadmanager.remove_all_downloads()
+ self.close(force=True)
+