def __init__(self, handle): activity.Activity.__init__(self, handle) self.props.max_participants = 1 self._web_view = Browser() try: from sugar.graphics.toolbarbox import ToolbarBox, ToolbarButton from sugar.activity.widgets import ActivityToolbarButton, StopButton, \ ShareButton from mybutton import MyActivityToolbarButton toolbar_box = ToolbarBox() activity_button = MyActivityToolbarButton(self) toolbar_box.toolbar.insert(activity_button, 0) activity_button.show() viewtoolbar = ViewToolbar(self) viewbutton = ToolbarButton(page=viewtoolbar, \ icon_name='camera') toolbar_box.toolbar.insert(viewbutton, -1) viewbutton.show() separator = gtk.SeparatorToolItem() #separator.props.draw = False #separator.set_expand(True) toolbar_box.toolbar.insert(separator, -1) separator.show() #lets reuse the code below navtoolbar = Toolbar(self._web_view) toolitem = gtk.ToolItem() navtoolbar._home.reparent(toolitem) toolbar_box.toolbar.insert(toolitem, -1) navtoolbar._home.show() toolitem.show() toolitem = gtk.ToolItem() navtoolbar._back.reparent(toolitem) toolbar_box.toolbar.insert(toolitem, -1) navtoolbar._back.show() toolitem.show() toolitem = gtk.ToolItem() navtoolbar._forward.reparent(toolitem) toolbar_box.toolbar.insert(toolitem, -1) navtoolbar._forward.show() toolitem.show() # we do not have collaboration features # make the share option insensitive self.max_participants = 1 separator = gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) toolbar_box.toolbar.insert(separator, -1) separator.show() stop_button = StopButton(self) stop_button.props.accelerator = '<Ctrl><Shift>Q' toolbar_box.toolbar.insert(stop_button, -1) stop_button.show() self.set_toolbar_box(toolbar_box) toolbar_box.show() except ImportError: toolbox = activity.ActivityToolbox(self) self.set_toolbox(toolbox) toolbox.show() toolbar = Toolbar(self._web_view) toolbox.add_toolbar(_('Navigation'), toolbar) toolbar.show() viewtoolbar = ViewToolbar(self) toolbox.add_toolbar(_('View'), viewtoolbar) viewtoolbar.show() toolbox.set_current_toolbar(1) self.set_canvas(self._web_view) self._web_view.show() self._web_view.load_uri(HOME)
class JukeboxActivity(activity.Activity): __gsignals__ = { 'playlist-finished': (GObject.SignalFlags.RUN_FIRST, None, []), } def __init__(self, handle): activity.Activity.__init__(self, handle) self.player = None self._alert = None self._playlist_jobject = None self.set_title(_('Jukebox Activity')) self.max_participants = 1 self._toolbar_box = ToolbarBox() activity_button = ActivityToolbarButton(self) activity_toolbar = activity_button.page self._toolbar_box.toolbar.insert(activity_button, 0) self.title_entry = activity_toolbar.title self._view_toolbar = ViewToolbar() self._view_toolbar.connect('go-fullscreen', self.__go_fullscreen_cb) self._view_toolbar.connect('toggle-playlist', self.__toggle_playlist_cb) view_toolbar_button = ToolbarButton( page=self._view_toolbar, icon_name='toolbar-view') self._view_toolbar.show() self._toolbar_box.toolbar.insert(view_toolbar_button, -1) view_toolbar_button.show() self._control_toolbar = Gtk.Toolbar() self._control_toolbar_button = ToolbarButton( page=self._control_toolbar, icon_name='media-playback-start') self._control_toolbar.show() self._toolbar_box.toolbar.insert(self._control_toolbar_button, -1) self._control_toolbar_button.hide() self.set_toolbar_box(self._toolbar_box) self._toolbar_box.show_all() self.connect('key_press_event', self.__key_press_event_cb) self.connect('playlist-finished', self.__playlist_finished_cb) # We want to be notified when the activity gets the focus or # loses it. When it is not active, we don't need to keep # reproducing the video self.connect('notify::active', self.__notify_active_cb) self._video_canvas = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self._playlist_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.playlist_widget = PlayList() self.playlist_widget.connect('play-index', self.__play_index_cb) self.playlist_widget.connect('missing-tracks', self.__missing_tracks_cb) self.playlist_widget.set_size_request( Gdk.Screen.width() * PLAYLIST_WIDTH_PROP, 0) self.playlist_widget.show() self._playlist_box.pack_start(self.playlist_widget, expand=True, fill=True, padding=0) self._playlist_toolbar = Gtk.Toolbar() move_up = ToolButton("go-up") move_up.set_tooltip(_("Move up")) move_up.connect("clicked", self._move_up_cb) self._playlist_toolbar.insert(move_up, 0) move_down = ToolButton("go-down") move_down.set_tooltip(_("Move down")) move_down.connect("clicked", self._move_down_cb) self._playlist_toolbar.insert(move_down, 1) self._playlist_box.pack_end(self._playlist_toolbar, False, False, 0) self._video_canvas.pack_start(self._playlist_box, False, False, 0) # Create the player just once logging.debug('Instantiating GstPlayer') self.player = GstPlayer() self.player.connect('eos', self.__player_eos_cb) self.player.connect('error', self.__player_error_cb) self.player.connect('play', self.__player_play_cb) self.control = Controls(self, self._toolbar_box.toolbar, self._control_toolbar) self._separator = Gtk.SeparatorToolItem() self._separator.props.draw = False self._separator.set_expand(True) self._separator.show() self._toolbar_box.toolbar.insert(self._separator, -1) self._stop = StopButton(self) self._toolbar_box.toolbar.insert(self._stop, -1) self._empty_widget = Gtk.Label(label="") self._empty_widget.show() self.videowidget = VideoWidget() self.set_canvas(self._video_canvas) self._init_view_area() self.show_all() # need hide the playlist by default self._playlist_box.hide() self._configure_cb() self.player.init_view_area(self.videowidget) self._volume_monitor = Gio.VolumeMonitor.get() self._volume_monitor.connect('mount-added', self.__mount_added_cb) self._volume_monitor.connect('mount-removed', self.__mount_removed_cb) if handle.object_id is None: # The activity was launched from scratch. We need to show # the Empty Widget self.playlist_widget.hide() emptypanel.show(self, 'activity-jukebox', _('No media'), _('Choose media files'), self.control.show_picker_cb) self.control.check_if_next_prev() Gdk.Screen.get_default().connect('size-changed', self._configure_cb) def _move_up_cb(self, button): self.playlist_widget.move_up() def _move_down_cb(self, button): self.playlist_widget.move_down() def _configure_cb(self, event=None): self._toolbar_box.toolbar.remove(self._stop) self._toolbar_box.toolbar.remove(self._separator) if Gdk.Screen.width() < Gdk.Screen.height(): self._control_toolbar_button.show() self._control_toolbar_button.set_expanded(True) self.control.update_layout(landscape=False) self._toolbar_box.toolbar.insert(self._separator, -1) else: self._control_toolbar_button.set_expanded(False) self._control_toolbar_button.hide() self.control.update_layout(landscape=True) self._toolbar_box.toolbar.insert(self._stop, -1) def __notify_active_cb(self, widget, event): """Sugar notify us that the activity is becoming active or inactive. When we are inactive, we stop the player if it is reproducing a video. """ logging.debug('JukeboxActivity notify::active signal received') if self.player.player.props.current_uri is not None and \ self.player.playing_video(): if not self.player.is_playing() and self.props.active: self.player.play() if self.player.is_playing() and not self.props.active: self.player.pause() def _init_view_area(self): """ Use a notebook with two pages, one empty an another with the videowidget """ self.view_area = Gtk.Notebook() self.view_area.set_show_tabs(False) self.view_area.append_page(self._empty_widget, None) self.view_area.append_page(self.videowidget, None) self._video_canvas.pack_end(self.view_area, expand=True, fill=True, padding=0) def _switch_canvas(self, show_video): """Show or hide the video visualization in the canvas. When hidden, the canvas is filled with an empty widget to ensure redrawing. """ if show_video: self.view_area.set_current_page(1) else: self.view_area.set_current_page(0) self._video_canvas.queue_draw() def __key_press_event_cb(self, widget, event): keyname = Gdk.keyval_name(event.keyval) logging.info("Keyname Press: %s, time: %s", keyname, event.time) if self.title_entry.has_focus(): return False if keyname == "space": self.control._button_clicked_cb(None) return True def __playlist_finished_cb(self, widget): self._switch_canvas(show_video=False) self._view_toolbar._show_playlist.set_active(True) self.unfullscreen() # Select the first stream to be played when Play button will # be pressed self.playlist_widget.set_current_playing(0) self.control.check_if_next_prev() def songchange(self, direction): current_playing = self.playlist_widget.get_current_playing() if direction == 'prev' and current_playing > 0: self.play_index(current_playing - 1) elif direction == 'next' and \ current_playing < len(self.playlist_widget._items) - 1: self.play_index(current_playing + 1) else: self.emit('playlist-finished') def play_index(self, index): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) path = self.playlist_widget._items[index]['path'] if self.playlist_widget.check_available_media(path): if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() else: self.songchange('next') def __play_index_cb(self, widget, index, path): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() def __player_eos_cb(self, widget): self.songchange('next') def _show_error_alert(self, title, msg=None): self._alert = ErrorAlert() self._alert.props.title = title if msg is not None: self._alert.props.msg = msg self.add_alert(self._alert) self._alert.connect('response', self._alert_cancel_cb) self._alert.show() def __mount_added_cb(self, volume_monitor, device): logging.debug('Mountpoint added. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __mount_removed_cb(self, volume_monitor, device): logging.debug('Mountpoint removed. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __missing_tracks_cb(self, widget, tracks): self._show_missing_tracks_alert(tracks) def _show_missing_tracks_alert(self, tracks): self._alert = Alert() title = _('%s tracks not found.') % len(tracks) self._alert.props.title = title icon = Icon(icon_name='dialog-cancel') self._alert.add_button(Gtk.ResponseType.CANCEL, _('Dismiss'), icon) icon.show() icon = Icon(icon_name='dialog-ok') self._alert.add_button(Gtk.ResponseType.APPLY, _('Details'), icon) icon.show() self.add_alert(self._alert) self._alert.connect( 'response', self.__missing_tracks_alert_response_cb, tracks) def __missing_tracks_alert_response_cb(self, alert, response_id, tracks): if response_id == Gtk.ResponseType.APPLY: vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.props.valign = Gtk.Align.CENTER label = Gtk.Label(label='') label.set_markup(_('<b>Missing tracks</b>')) vbox.pack_start(label, False, False, 15) for track in tracks: label = Gtk.Label(label=track['path']) vbox.add(label) _missing_tracks = Gtk.ScrolledWindow() _missing_tracks.add_with_viewport(vbox) _missing_tracks.show_all() self.view_area.append_page(_missing_tracks, None) self.view_area.set_current_page(2) self.remove_alert(alert) def _alert_cancel_cb(self, alert, response_id): self.remove_alert(alert) def __player_play_cb(self, widget): # Do not show the visualization widget if we are playing just # an audio stream def callback(): if self.player.playing_video(): self._switch_canvas(True) else: self._switch_canvas(False) return False # HACK: we need a timeout here because gstreamer returns # n-video = 0 if we call it immediately GObject.timeout_add(1000, callback) def __player_error_cb(self, widget, message, detail): self.player.stop() self.control.set_disabled() logging.error('ERROR MESSAGE: %s', message) logging.error('ERROR DETAIL: %s', detail) file_path = self.playlist_widget._items[ self.playlist_widget.get_current_playing()]['path'] mimetype = mime.get_for_file(file_path) title = _('Error') msg = _('This "%s" file can\'t be played') % mimetype self._switch_canvas(False) self._show_error_alert(title, msg) def can_close(self): # We need to put the Gst.State in NULL so gstreamer can # cleanup the pipeline self.player.stop() return True def read_file(self, file_path): """Load a file from the datastore on activity start.""" logging.debug('JukeBoxAtivity.read_file: %s', file_path) title = self.metadata['title'] self.playlist_widget.load_file(file_path, title) self._view_toolbar._show_playlist.set_active(True) def write_file(self, file_path): def write_playlist_to_file(file_path): """Open the file at file_path and write the playlist. It is saved in audio/x-mpegurl format. """ list_file = open(file_path, 'w') for uri in self.playlist_widget._items: list_file.write('#EXTINF:%s\n' % uri['title']) list_file.write('%s\n' % uri['path']) list_file.close() if not self.metadata['mime_type']: self.metadata['mime_type'] = 'audio/x-mpegurl' if self.metadata['mime_type'] == 'audio/x-mpegurl': write_playlist_to_file(file_path) else: if self._playlist_jobject is None: self._playlist_jobject = \ self.playlist_widget.create_playlist_jobject() # Add the playlist to the playlist jobject description. # This is only done if the activity was not started from a # playlist or from scratch: description = '' for uri in self.playlist_widget._items: description += '%s\n' % uri['title'] self._playlist_jobject.metadata['description'] = description write_playlist_to_file(self._playlist_jobject.file_path) datastore.write(self._playlist_jobject) def __go_fullscreen_cb(self, toolbar): self.fullscreen() def __toggle_playlist_cb(self, toolbar): if self._view_toolbar._show_playlist.get_active(): self._playlist_box.show_all() else: self._playlist_box.hide() self._video_canvas.queue_draw()
class WebActivity(activity.Activity): def __init__(self, handle): activity.Activity.__init__(self, handle) _logger.debug('Starting the web activity') # figure out if we're an SSB self.is_ssb = ssb.get_is_ssb(self) self._browser = Browser() _set_accept_languages() _seed_xs_cookie() _set_dbus_globals(self.get_bundle_id()) # don't pick up the sugar theme - use the native mozilla one instead cls = components.classes['@mozilla.org/preferences-service;1'] pref_service = cls.getService(components.interfaces.nsIPrefService) branch = pref_service.getBranch("mozilla.widget.") branch.setBoolPref("disable-native-theme", True) toolbox = activity.ActivityToolbox(self) self._edit_toolbar = EditToolbar(self._browser) toolbox.add_toolbar(_('Edit'), self._edit_toolbar) self._edit_toolbar.show() self._web_toolbar = WebToolbar(self) toolbox.add_toolbar(_('Browse'), self._web_toolbar) self._web_toolbar.show() self._tray = HTray() self.set_tray(self._tray, gtk.POS_BOTTOM) self._tray.show() self._view_toolbar = ViewToolbar(self) toolbox.add_toolbar(_('View'), self._view_toolbar) self._view_toolbar.show() # the bookmarklet bar doesn't show up if empty self._bm_toolbar = None self.set_toolbox(toolbox) toolbox.show() self.set_canvas(self._browser) self._browser.show() self._browser.history.connect('session-link-changed', self._session_history_changed_cb) self._web_toolbar.connect('add-link', self._link_add_button_cb) self._browser.connect("notify::title", self._title_changed_cb) self._bm_store = bookmarklets.get_store() self._bm_store.connect('add_bookmarklet', self._add_bookmarklet_cb) self._bm_store.connect('overwrite_bookmarklet', self._overwrite_bookmarklet_cb) for name in self._bm_store.list(): self._add_bookmarklet(name) 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 self.is_ssb: # set permanent homepage for SSBs f = open(os.path.join(activity.get_bundle_path(), 'data/homepage')) self.homepage = f.read() f.close() # enable userscript saving self._browser.userscript.connect('userscript-found', self._userscript_found_cb) # enable userscript injecting self._browser.userscript.connect('userscript-inject', self._userscript_inject_cb) 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...') 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, identifier, initiator, type, service, params, state): _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d' %(identifier, 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( identifier) self.tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], identifier, 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 self.is_ssb: self._browser.load_uri(self.homepage) elif os.path.isfile(_LIBRARY_PATH): self._browser.load_uri('file://' + _LIBRARY_PATH) else: default_page = os.path.join(activity.get_bundle_path(), "data/index.html") self._browser.load_uri(default_page) 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.') elif self.metadata['mime_type'] == 'application/zip': z = zipfile.ZipFile(file_path, 'r') html = None for i in z.namelist(): if i.endswith('.html') or i.endswith('.htm'): html = i if i == 'index.html': break if file_name != None: self._browser.load_uri('jar:file://%!%s' % (file_path, html)) else: _logger.error('Open jar file: No html file to be opened') 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 save_document(self): logging.debug('Saving document to %s' % bundle_path) cls = components.classes[ \ '@mozilla.org/embedding/browser/nsWebBrowserPersist;1'] persist = cls.createInstance(interfaces.nsIWebBrowserPersist) persist.persistFlags = interfaces.nsIWebBrowserPersist \ .PERSIST_FLAGS_REPLACE_EXISTING_FILES local = components.classes["@mozilla.org/file/local;1"] local_file = local.createInstance(interfaces.nsILocalFile) local_data = local.createInstance(interfaces.nsILocalFile) temp_dir = tempfile.mkdtemp() local_file.initWithPath(os.path.join(temp_dir, 'index.html')) local_data.initWithPath(os.path.join(temp_dir, 'data')) persist.saveDocument(self._browser.dom_window.document, local_file, local_data, None, 0, 0) bundle_path = os.path.join(temp_dir, 'bundle.jar') bundle = zipfile.ZipFile(bundle_path, 'w') bundle.write(local_file.path) for i in os.listdir(local_data.path): bundle.write(os.path.join(local_data.path, i), zipfile.ZIP_DEFLATED) bundle.close() jobject = datastore.create() jobject.metadata['title'] = self.title jobject.metadata['mime_type'] = 'application/zip' jobject.metadata['icon-color'] = profile.get_color().to_string() jobject.metadata['activity'] = 'org.laptop.WebActivity' jobject.file_path = bundle_path datastore.write(jobject) activity.show_object_in_journal(jobject.object_id) # cleanup shutil.rmtree(temp_dir) 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) == "d": _logger.debug('keyboard: Add link: %s.' % self.current) self._add_link() return True elif gtk.gdk.keyval_name(event.keyval) == "f": _logger.debug('keyboard: Find') self.toolbox.set_current_toolbar(_TOOLBAR_EDIT) self._edit_toolbar.search_entry.grab_focus() return True elif gtk.gdk.keyval_name(event.keyval) == "l": _logger.debug('keyboard: Focus url entry') self.toolbox.set_current_toolbar(_TOOLBAR_BROWSE) self._web_toolbar.entry.grab_focus() 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 return False def _add_bookmarklet(self, name): '''add bookmarklet button and, if needed, the toolbar''' if self._bm_toolbar is None: self._bm_toolbar = BookmarkletToolbar(self) self.toolbox.add_toolbar(_('Bookmarklets'), self._bm_toolbar) self._bm_toolbar.show() if name not in self._bm_toolbar.bookmarklets: self._bm_toolbar.add_bookmarklet(name) return self._bm_toolbar.bookmarklets[name] def _add_bookmarklet_cb(self, store, name): '''receive name of new bookmarklet from the store''' bm = self._add_bookmarklet(name) bm.flash() self.toolbox.set_current_toolbar(_TOOLBAR_BOOKMARKLETS) def _overwrite_bookmarklet_cb(self, store, name, url): '''Ask for confirmation''' alert = ConfirmationAlert() alert.props.title = _('Add bookmarklet') alert.props.msg = _('"%s" already exists. Overwrite?') % name alert.connect('response', self._overwrite_bookmarklet_response_cb) # send the arguments through the alert object alert._bm = (name, url) self.add_alert(alert) def _overwrite_bookmarklet_response_cb(self, alert, response_id): self.remove_alert(alert) name, url = alert._bm # unpack the argument if response_id is gtk.RESPONSE_OK: self._bm_store.remove(name) self._bm_store.add(name, url) def _userscript_found_cb(self, listener, location): '''Ask user whether to install the userscript''' alert = ConfirmationAlert() alert.props.title = _('Add userscript') if usercode.script_exists(location): alert.props.msg = _('Userscript already exists. Overwrite?') else: alert.props.msg = _('Do you want to add this userscript?') alert.connect('response', self._userscript_found_response_cb) # send the argument through the alert object alert._location = location self.add_alert(alert) def _userscript_found_response_cb(self, alert, response_id): self.remove_alert(alert) if response_id is gtk.RESPONSE_OK: usercode.add_script(alert._location) def _userscript_inject_cb(self, listener, script_path): logging.debug('Injecting %s' % script_path) usercode.Injector(script_path).attach_to(self._browser.dom_window) 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 buf = self._get_screenshot() timestamp = time.time() self.model.add_link(self.current, self.webtitle, buf, 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(buf), 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, buf, color, title, owner, index, hash): ''' add a link to the tray ''' item = LinkButton(url, buf, 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._view_toolbar.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._view_toolbar.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 _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) buf = self.get_buffer(screenshot) return buf 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) #def handle_view_source(self): # logging.debug('##### local view source') # logging.debug('@@@@@ %s' % usercode.STYLE_PATH) # view_source = viewsource.ViewSource(self.get_xid(), # self.get_bundle_path(), # usercode.STYLE_PATH, # self.get_title()) # view_source.show() def get_document_path(self, async_cb, async_err_cb): self._browser.get_source(async_cb, async_err_cb)
class JukeboxActivity(activity.Activity): __gsignals__ = { 'playlist-finished': (GObject.SignalFlags.RUN_FIRST, None, []), } def __init__(self, handle): activity.Activity.__init__(self, handle) self.player = None self._alert = None self._playlist_jobject = None self.set_title(_('Jukebox Activity')) self.max_participants = 1 toolbar_box = ToolbarBox() activity_button = ActivityToolbarButton(self) activity_toolbar = activity_button.page toolbar_box.toolbar.insert(activity_button, 0) self.title_entry = activity_toolbar.title self._view_toolbar = ViewToolbar() self._view_toolbar.connect('go-fullscreen', self.__go_fullscreen_cb) self._view_toolbar.connect('toggle-playlist', self.__toggle_playlist_cb) view_toolbar_button = ToolbarButton(page=self._view_toolbar, icon_name='toolbar-view') self._view_toolbar.show() toolbar_box.toolbar.insert(view_toolbar_button, -1) view_toolbar_button.show() self._control_toolbar = Gtk.Toolbar() self._control_toolbar_button = ToolbarButton( page=self._control_toolbar, icon_name='media-playback-start') self._control_toolbar.show() toolbar_box.toolbar.insert(self._control_toolbar_button, -1) self._control_toolbar_button.hide() self.set_toolbar_box(toolbar_box) toolbar_box.show_all() self.connect('key_press_event', self.__key_press_event_cb) self.connect('playlist-finished', self.__playlist_finished_cb) # We want to be notified when the activity gets the focus or # loses it. When it is not active, we don't need to keep # reproducing the video self.connect('notify::active', self.__notify_active_cb) self._video_canvas = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self._playlist_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.playlist_widget = PlayList() self.playlist_widget.connect('play-index', self.__play_index_cb) self.playlist_widget.connect('missing-tracks', self.__missing_tracks_cb) self.playlist_widget.set_size_request( Gdk.Screen.width() * PLAYLIST_WIDTH_PROP, 0) self.playlist_widget.show() self._playlist_box.pack_start(self.playlist_widget, expand=True, fill=True, padding=0) self._playlist_toolbar = Gtk.Toolbar() move_up = ToolButton("go-up") move_up.set_tooltip(_("Move up")) move_up.connect("clicked", self._move_up_cb) self._playlist_toolbar.insert(move_up, 0) move_down = ToolButton("go-down") move_down.set_tooltip(_("Move down")) move_down.connect("clicked", self._move_down_cb) self._playlist_toolbar.insert(move_down, 1) self._playlist_box.pack_end(self._playlist_toolbar, False, False, 0) self._video_canvas.pack_start(self._playlist_box, False, False, 0) # Create the player just once logging.debug('Instantiating GstPlayer') self.player = GstPlayer() self.player.connect('eos', self.__player_eos_cb) self.player.connect('error', self.__player_error_cb) self.player.connect('play', self.__player_play_cb) self.control = Controls(self, toolbar_box.toolbar, self._control_toolbar) self._separator = Gtk.SeparatorToolItem() self._separator.props.draw = False self._separator.set_expand(True) self._separator.show() toolbar_box.toolbar.insert(self._separator, -1) self._stop = StopButton(self) toolbar_box.toolbar.insert(self._stop, -1) self._empty_widget = Gtk.Label(label="") self._empty_widget.show() self.videowidget = VideoWidget() self.set_canvas(self._video_canvas) self._init_view_area() self.show_all() # need hide the playlist by default self._playlist_box.hide() self._configure_cb() self.player.init_view_area(self.videowidget) self._volume_monitor = Gio.VolumeMonitor.get() self._volume_monitor.connect('mount-added', self.__mount_added_cb) self._volume_monitor.connect('mount-removed', self.__mount_removed_cb) if handle.object_id is None: # The activity was launched from scratch. We need to show # the Empty Widget self.playlist_widget.hide() emptypanel.show(self, 'activity-jukebox', _('No media'), _('Choose media files'), self.control.show_picker_cb) self.control.check_if_next_prev() Gdk.Screen.get_default().connect('size-changed', self._configure_cb) def _move_up_cb(self, button): self.playlist_widget.move_up() def _move_down_cb(self, button): self.playlist_widget.move_down() def _configure_cb(self, event=None): toolbar = self.get_toolbar_box().toolbar toolbar.remove(self._stop) toolbar.remove(self._separator) if Gdk.Screen.width() < Gdk.Screen.height(): self._control_toolbar_button.show() self._control_toolbar_button.set_expanded(True) self.control.update_layout(landscape=False) toolbar.insert(self._separator, -1) else: self._control_toolbar_button.set_expanded(False) self._control_toolbar_button.hide() self.control.update_layout(landscape=True) toolbar.insert(self._stop, -1) def __notify_active_cb(self, widget, event): """Sugar notify us that the activity is becoming active or inactive. When we are inactive, we stop the player if it is reproducing a video. """ logging.debug('JukeboxActivity notify::active signal received') if self.player.player.props.current_uri is not None and \ self.player.playing_video(): if not self.player.is_playing() and self.props.active: self.player.play() if self.player.is_playing() and not self.props.active: self.player.pause() def _init_view_area(self): """ Use a notebook with two pages, one empty an another with the videowidget """ self.view_area = Gtk.Notebook() self.view_area.set_show_tabs(False) self.view_area.append_page(self._empty_widget, None) self.view_area.append_page(self.videowidget, None) self._video_canvas.pack_end(self.view_area, expand=True, fill=True, padding=0) def _switch_canvas(self, show_video): """Show or hide the video visualization in the canvas. When hidden, the canvas is filled with an empty widget to ensure redrawing. """ if show_video: self.view_area.set_current_page(1) else: self.view_area.set_current_page(0) self._video_canvas.queue_draw() def __key_press_event_cb(self, widget, event): keyname = Gdk.keyval_name(event.keyval) logging.info("Keyname Press: %s, time: %s", keyname, event.time) if self.title_entry.has_focus(): return False if keyname == "space": self.control._button_clicked_cb(None) return True def __playlist_finished_cb(self, widget): self._switch_canvas(show_video=False) self._view_toolbar._show_playlist.set_active(True) self.unfullscreen() # Select the first stream to be played when Play button will # be pressed self.playlist_widget.set_current_playing(0) self.control.check_if_next_prev() def songchange(self, direction): current_playing = self.playlist_widget.get_current_playing() if direction == 'prev' and current_playing > 0: self.play_index(current_playing - 1) elif direction == 'next' and \ current_playing < len(self.playlist_widget._items) - 1: self.play_index(current_playing + 1) else: self.emit('playlist-finished') def play_index(self, index): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) path = self.playlist_widget._items[index]['path'] if self.playlist_widget.check_available_media(path): if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() else: self.songchange('next') def __play_index_cb(self, widget, index, path): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() def __player_eos_cb(self, widget): self.songchange('next') def _show_error_alert(self, title, msg=None): self._alert = ErrorAlert() self._alert.props.title = title if msg is not None: self._alert.props.msg = msg self.add_alert(self._alert) self._alert.connect('response', self._alert_cancel_cb) self._alert.show() def __mount_added_cb(self, volume_monitor, device): logging.debug('Mountpoint added. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __mount_removed_cb(self, volume_monitor, device): logging.debug('Mountpoint removed. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __missing_tracks_cb(self, widget, tracks): self._show_missing_tracks_alert(tracks) def _show_missing_tracks_alert(self, tracks): self._alert = Alert() title = _('%s tracks not found.') % len(tracks) self._alert.props.title = title icon = Icon(icon_name='dialog-cancel') self._alert.add_button(Gtk.ResponseType.CANCEL, _('Dismiss'), icon) icon.show() icon = Icon(icon_name='dialog-ok') self._alert.add_button(Gtk.ResponseType.APPLY, _('Details'), icon) icon.show() self.add_alert(self._alert) self._alert.connect('response', self.__missing_tracks_alert_response_cb, tracks) def __missing_tracks_alert_response_cb(self, alert, response_id, tracks): if response_id == Gtk.ResponseType.APPLY: vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.props.valign = Gtk.Align.CENTER label = Gtk.Label(label='') label.set_markup(_('<b>Missing tracks</b>')) vbox.pack_start(label, False, False, 15) for track in tracks: label = Gtk.Label(label=track['path']) vbox.add(label) _missing_tracks = Gtk.ScrolledWindow() _missing_tracks.add_with_viewport(vbox) _missing_tracks.show_all() self.view_area.append_page(_missing_tracks, None) self.view_area.set_current_page(2) self.remove_alert(alert) def _alert_cancel_cb(self, alert, response_id): self.remove_alert(alert) def __player_play_cb(self, widget): # Do not show the visualization widget if we are playing just # an audio stream def callback(): if self.player.playing_video(): self._switch_canvas(True) else: self._switch_canvas(False) return False # HACK: we need a timeout here because gstreamer returns # n-video = 0 if we call it immediately GObject.timeout_add(1000, callback) def __player_error_cb(self, widget, message, detail): self.player.stop() self.control.set_disabled() logging.error('ERROR MESSAGE: %s', message) logging.error('ERROR DETAIL: %s', detail) file_path = self.playlist_widget._items[ self.playlist_widget.get_current_playing()]['path'] mimetype = mime.get_for_file(file_path) title = _('Error') msg = _('This "%s" file can\'t be played') % mimetype self._switch_canvas(False) self._show_error_alert(title, msg) def can_close(self): # We need to put the Gst.State in NULL so gstreamer can # cleanup the pipeline self.player.stop() return True def read_file(self, file_path): """Load a file from the datastore on activity start.""" logging.debug('JukeBoxAtivity.read_file: %s', file_path) title = self.metadata['title'] self.playlist_widget.load_file(file_path, title) self._view_toolbar._show_playlist.set_active(True) def write_file(self, file_path): def write_playlist_to_file(file_path): """Open the file at file_path and write the playlist. It is saved in audio/x-mpegurl format. """ list_file = open(file_path, 'w') for uri in self.playlist_widget._items: list_file.write('#EXTINF:%s\n' % uri['title']) list_file.write('%s\n' % uri['path']) list_file.close() if not self.metadata['mime_type']: self.metadata['mime_type'] = 'audio/x-mpegurl' if self.metadata['mime_type'] == 'audio/x-mpegurl': write_playlist_to_file(file_path) else: if self._playlist_jobject is None: self._playlist_jobject = \ self.playlist_widget.create_playlist_jobject() # Add the playlist to the playlist jobject description. # This is only done if the activity was not started from a # playlist or from scratch: description = '' for uri in self.playlist_widget._items: description += '%s\n' % uri['title'] self._playlist_jobject.metadata['description'] = description write_playlist_to_file(self._playlist_jobject.file_path) datastore.write(self._playlist_jobject) def __go_fullscreen_cb(self, toolbar): self.fullscreen() def __toggle_playlist_cb(self, toolbar): if self._view_toolbar._show_playlist.get_active(): self._playlist_box.show_all() else: self._playlist_box.hide() self._video_canvas.queue_draw()
def __init__(self, handle): activity.Activity.__init__(self, handle) self.props.max_participants = 1 self._web_view = Browser() try: from sugar.graphics.toolbarbox import ToolbarBox, ToolbarButton from sugar.activity.widgets import ActivityToolbarButton, StopButton, ShareButton from mybutton import MyActivityToolbarButton toolbar_box = ToolbarBox() activity_button = MyActivityToolbarButton(self) toolbar_box.toolbar.insert(activity_button, 0) activity_button.show() viewtoolbar = ViewToolbar(self) viewbutton = ToolbarButton(page=viewtoolbar, icon_name="camera") toolbar_box.toolbar.insert(viewbutton, -1) viewbutton.show() separator = gtk.SeparatorToolItem() # separator.props.draw = False # separator.set_expand(True) toolbar_box.toolbar.insert(separator, -1) separator.show() # lets reuse the code below navtoolbar = Toolbar(self._web_view) toolitem = gtk.ToolItem() navtoolbar._home.reparent(toolitem) toolbar_box.toolbar.insert(toolitem, -1) navtoolbar._home.show() toolitem.show() toolitem = gtk.ToolItem() navtoolbar._back.reparent(toolitem) toolbar_box.toolbar.insert(toolitem, -1) navtoolbar._back.show() toolitem.show() toolitem = gtk.ToolItem() navtoolbar._forward.reparent(toolitem) toolbar_box.toolbar.insert(toolitem, -1) navtoolbar._forward.show() toolitem.show() # we do not have collaboration features # make the share option insensitive self.max_participants = 1 separator = gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) toolbar_box.toolbar.insert(separator, -1) separator.show() stop_button = StopButton(self) stop_button.props.accelerator = "<Ctrl><Shift>Q" toolbar_box.toolbar.insert(stop_button, -1) stop_button.show() self.set_toolbar_box(toolbar_box) toolbar_box.show() except ImportError: toolbox = activity.ActivityToolbox(self) self.set_toolbox(toolbox) toolbox.show() toolbar = Toolbar(self._web_view) toolbox.add_toolbar(_("Navigation"), toolbar) toolbar.show() viewtoolbar = ViewToolbar(self) toolbox.add_toolbar(_("View"), viewtoolbar) viewtoolbar.show() toolbox.set_current_toolbar(1) self.set_canvas(self._web_view) self._web_view.show() self._web_view.load_uri(HOME)