Exemplo n.º 1
0
class WebActivity(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        _logger.debug('Starting the web activity')

        session = WebKit.get_default_session()
        session.set_property('accept-language-auto', True)
        session.set_property('ssl-use-system-ca-file', True)
        session.set_property('ssl-strict', False)

        # By default, cookies are not stored persistently, we have to
        # add a cookie jar so that they get saved to disk.  We use one
        # with a SQlite database:
        cookie_jar = SoupGNOME.CookieJarSqlite(filename=_cookies_db_path,
                                               read_only=False)
        session.add_feature(cookie_jar)

        _seed_xs_cookie(cookie_jar)

        # FIXME
        # downloadmanager.remove_old_parts()

        self._force_close = False
        self._tabbed_view = TabbedView()
        self._tabbed_view.connect('focus-url-entry', self._on_focus_url_entry)
        self._tabbed_view.connect('switch-page', self.__switch_page_cb)

        self._tray = HTray()
        self.set_tray(self._tray, Gtk.PositionType.BOTTOM)

        self._primary_toolbar = PrimaryToolbar(self._tabbed_view, self)
        self._edit_toolbar = EditToolbar(self)
        self._view_toolbar = ViewToolbar(self)

        self._primary_toolbar.connect('add-link', self._link_add_button_cb)

        self._primary_toolbar.connect('go-home', self._go_home_button_cb)

        self._primary_toolbar.connect('go-library', self._go_library_button_cb)

        self._primary_toolbar.connect('set-home', self._set_home_button_cb)

        self._primary_toolbar.connect('reset-home', self._reset_home_button_cb)

        self._edit_toolbar_button = ToolbarButton(
            page=self._edit_toolbar, icon_name='toolbar-edit')

        self._primary_toolbar.toolbar.insert(
            self._edit_toolbar_button, 1)

        view_toolbar_button = ToolbarButton(
            page=self._view_toolbar, icon_name='toolbar-view')
        self._primary_toolbar.toolbar.insert(
            view_toolbar_button, 2)

        self._primary_toolbar.show_all()
        self.set_toolbar_box(self._primary_toolbar)

        self.set_canvas(self._tabbed_view)
        self._tabbed_view.show()

        self.model = Model()
        self.model.connect('add_link', self._add_link_model_cb)

        self.connect('key-press-event', self._key_press_cb)

        if handle.uri:
            self._tabbed_view.current_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._tabbed_view.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.get_shared_activity() is not None:
            _logger.debug('shared: %s', self.get_shared())
            # 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')

        # README: this is a workaround to remove old temp file
        # http://bugs.sugarlabs.org/ticket/3973
        self._cleanup_temp_files()

    def _cleanup_temp_files(self):
        """Removes temporary files generated by Download Manager that
        were cancelled by the user or failed for any reason.

        There is a bug in GLib that makes this to happen:
            https://bugzilla.gnome.org/show_bug.cgi?id=629301
        """

        try:
            uptime_proc = open('/proc/uptime', 'r').read()
            uptime = int(float(uptime_proc.split()[0]))
        except EnvironmentError:
            logging.warning('/proc/uptime could not be read')
            uptime = None

        temp_path = os.path.join(self.get_activity_root(), 'instance')
        now = int(time.time())
        cutoff = now - 24 * 60 * 60  # yesterday
        if uptime is not None:
            boot_time = now - uptime
            cutoff = max(cutoff, boot_time)

        for f in os.listdir(temp_path):
            if f.startswith('.goutputstream-'):
                fpath = os.path.join(temp_path, f)
                mtime = int(os.path.getmtime(fpath))
                if mtime < cutoff:
                    logging.warning('Removing old temporary file: %s', fpath)
                    try:
                        os.remove(fpath)
                    except EnvironmentError:
                        logging.error('Temporary file could not be '
                                      'removed: %s', fpath)

    def _on_focus_url_entry(self, gobject):
        self._primary_toolbar.entry.grab_focus()

    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.get_shared_activity() is None:
            _logger.debug('Failed to share or join activity')
            return

        bus_name, conn_path, channel_paths = \
            self.get_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.get_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 _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'],
                                      link.get('notes'))
            logging.debug('########## reading %s', data)
            self._tabbed_view.set_history(self.model.data['history'])
            for number, tab in enumerate(self.model.data['currents']):
                tab_page = self._tabbed_view.get_nth_page(number)
                tab_page.browser.set_history_index(tab['history_index'])
                zoom_level = tab.get('zoom_level')
                if zoom_level is not None:
                    tab_page.browser.set_zoom_level(zoom_level)
                tab_page.browser.grab_focus()

            self._tabbed_view.set_current_page(self.model.data['current_tab'])

        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._tabbed_view.props.current_browser.load_uri(uris[0])
            else:
                _logger.error('Open uri-list: Does not support'
                              'list of multiple uris by now.')
        else:
            file_uri = 'file://' + file_path
            self._tabbed_view.props.current_browser.load_uri(file_uri)
            self._tabbed_view.props.current_browser.grab_focus()

    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':

            browser = self._tabbed_view.current_browser

            if not self._jobject.metadata['title_set_by_user'] == '1':
                if browser.props.title is None:
                    self.metadata['title'] = _('Untitled')
                else:
                    self.metadata['title'] = browser.props.title

            self.model.data['history'] = self._tabbed_view.get_history()
            current_tab = self._tabbed_view.get_current_page()
            self.model.data['current_tab'] = current_tab

            self.model.data['currents'] = []
            for n in range(0, self._tabbed_view.get_n_pages()):
                tab_page = self._tabbed_view.get_nth_page(n)
                n_browser = tab_page.browser
                if n_browser is not None:
                    uri = n_browser.get_uri()
                    history_index = n_browser.get_history_index()
                    info = {'title': n_browser.props.title, 'url': uri,
                            'history_index': history_index,
                            'zoom_level': n_browser.get_zoom_level()}

                    self.model.data['currents'].append(info)

            f = open(file_path, 'w')
            try:
                logging.debug('########## writing %s', self.model.serialize())
                f.write(self.model.serialize())
            finally:
                f.close()

    def _link_add_button_cb(self, button):
        self._add_link()

    def _go_home_button_cb(self, button):
        self._tabbed_view.load_homepage()

    def _go_library_button_cb(self, button):
        self._tabbed_view.load_homepage(ignore_gconf=True)

    def _set_home_button_cb(self, button):
        self._tabbed_view.set_homepage()
        self._alert(_('The initial page was configured'))

    def _reset_home_button_cb(self, button):
        self._tabbed_view.reset_homepage()
        self._alert(_('The default initial page was configured'))

    def _alert(self, title, text=None):
        alert = NotifyAlert(timeout=5)
        alert.props.title = title
        alert.props.msg = text
        self.add_alert(alert)
        alert.connect('response', self._alert_cancel_cb)
        alert.show()

    def _alert_cancel_cb(self, alert, response_id):
        self.remove_alert(alert)

    def _key_press_cb(self, widget, event):
        key_name = Gdk.keyval_name(event.keyval)
        browser = self._tabbed_view.props.current_browser

        if event.get_state() & Gdk.ModifierType.CONTROL_MASK:

            if key_name == 'd':
                self._add_link()
            elif key_name == 'f':
                _logger.debug('keyboard: Find')
                self._edit_toolbar_button.set_expanded(True)
                self._edit_toolbar.search_entry.grab_focus()
            elif key_name == 'l':
                _logger.debug('keyboard: Focus url entry')
                self._primary_toolbar.entry.grab_focus()
            elif key_name == 'minus':
                _logger.debug('keyboard: Zoom out')
                browser.zoom_out()
            elif key_name in ['plus', 'equal']:
                _logger.debug('keyboard: Zoom in')
                browser.zoom_in()
            elif key_name == '0':
                _logger.debug('keyboard: Actual size')
                browser.set_zoom_level(ZOOM_ORIGINAL)
            elif key_name == 'Left':
                _logger.debug('keyboard: Go back')
                browser.go_back()
            elif key_name == 'Right':
                _logger.debug('keyboard: Go forward')
                browser.go_forward()
            elif key_name == 'r':
                _logger.debug('keyboard: Reload')
                browser.reload()
            elif Gdk.keyval_name(event.keyval) == "t":
                self._tabbed_view.add_tab()
            elif key_name == 'w':
                _logger.debug('keyboard: close tab')
                self._tabbed_view.close_tab()
            elif key_name == "Tab":
                _logger.debug('keyboard: next tab')
                current_index = self._tabbed_view.get_current_page()
                if current_index == self._tabbed_view.get_n_pages() - 1:
                    self._tabbed_view.set_current_page(0)
                else:
                    self._tabbed_view.set_current_page(current_index + 1)
            elif event.get_state() & Gdk.ModifierType.SHIFT_MASK:
                if key_name == "ISO_Left_Tab":
                    _logger.debug('keyboard: previous tab')
                    current_index = self._tabbed_view.get_current_page()
                    last_index = self._tabbed_view.get_n_pages()
                    if current_index == 0:
                        self._tabbed_view.set_current_page(last_index - 1)
                    else:
                        self._tabbed_view.set_current_page(current_index - 1)
            else:
                return False

            return True

        elif key_name in ('KP_Up', 'KP_Down', 'KP_Left', 'KP_Right'):
            scrolled_window = browser.get_parent()

            if key_name in ('KP_Up', 'KP_Down'):
                adjustment = scrolled_window.get_vadjustment()
            elif key_name in ('KP_Left', 'KP_Right'):
                adjustment = scrolled_window.get_hadjustment()
            value = adjustment.get_value()
            step = adjustment.get_step_increment()

            if key_name in ('KP_Up', 'KP_Left'):
                adjustment.set_value(value - step)
            elif key_name in ('KP_Down', 'KP_Right'):
                adjustment.set_value(value + step)

            return True

        elif key_name == 'Escape':
            status = browser.get_load_status()
            loading = WebKit.LoadStatus.PROVISIONAL <= status \
                < WebKit.LoadStatus.FINISHED
            if loading:
                _logger.debug('keyboard: Stop loading')
                browser.stop_loading()

        return False

    def _add_link(self):
        ''' take screenshot and add link info to the model '''

        browser = self._tabbed_view.props.current_browser
        ui_uri = browser.get_uri()

        for link in self.model.data['shared_links']:
            if link['hash'] == sha1(ui_uri).hexdigest():
                _logger.debug('_add_link: link exist already a=%s b=%s',
                              link['hash'], sha1(ui_uri).hexdigest())
                return
        buf = self._get_screenshot()
        timestamp = time.time()
        self.model.add_link(ui_uri, browser.props.title, buf,
                            profile.get_nick_name(),
                            profile.get_color().to_string(), timestamp)

        if self.messenger is not None:
            self.messenger._add_link(ui_uri, browser.props.title,
                                     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'],
                              link.get('notes'))

    def _add_link_totray(self, url, buf, color, title, owner, index, hash,
                         notes=None):
        ''' add a link to the tray '''
        item = LinkButton(buf, color, title, owner, hash, notes)
        item.connect('clicked', self._link_clicked_cb, url)
        item.connect('remove_link', self._link_removed_cb)
        item.notes_changed_signal.connect(self.__link_notes_changed)
        # use index to add to the tray
        self._tray.add_item(item, index)
        item.show()
        self._view_toolbar.traybutton.props.sensitive = True
        self._view_toolbar.traybutton.props.active = True
        self._view_toolbar.update_traybutton_tooltip()

    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
            self._view_toolbar.traybutton.props.active = False
            self._view_toolbar.update_traybutton_tooltip()

    def __link_notes_changed(self, button, hash, notes):
        self.model.change_link_notes(hash, notes)

    def _link_clicked_cb(self, button, url):
        ''' an item of the link tray has been clicked '''
        self._tabbed_view.props.current_browser.load_uri(url)

    def _get_screenshot(self):
        browser = self._tabbed_view.props.current_browser
        window = browser.get_window()
        width, height = window.get_width(), window.get_height()

        thumb_width, thumb_height = style.zoom(100), style.zoom(80)

        thumb_surface = Gdk.Window.create_similar_surface(
            window, cairo.CONTENT_COLOR, thumb_width, thumb_height)

        cairo_context = cairo.Context(thumb_surface)
        thumb_scale_w = thumb_width * 1.0 / width
        thumb_scale_h = thumb_height * 1.0 / height
        cairo_context.scale(thumb_scale_w, thumb_scale_h)
        Gdk.cairo_set_source_window(cairo_context, window, 0, 0)
        cairo_context.paint()

        thumb_str = StringIO.StringIO()
        thumb_surface.write_to_png(thumb_str)
        return thumb_str.getvalue()

    def can_close(self):
        if self._force_close:
            return True
        elif downloadmanager.can_quit():
            return True
        else:
            alert = Alert()
            alert.props.title = ngettext('Download in progress',
                                         'Downloads in progress',
                                         downloadmanager.num_downloads())
            message = ngettext('Stopping now will erase your download',
                               'Stopping now will erase your downloads',
                               downloadmanager.num_downloads())
            alert.props.msg = message
            cancel_icon = Icon(icon_name='dialog-cancel')
            cancel_label = ngettext('Continue download', 'Continue downloads',
                                    downloadmanager.num_downloads())
            alert.add_button(Gtk.ResponseType.CANCEL, cancel_label,
                             cancel_icon)
            stop_icon = Icon(icon_name='dialog-ok')
            alert.add_button(Gtk.ResponseType.OK, _('Stop'), stop_icon)
            stop_icon.show()
            self.add_alert(alert)
            alert.connect('response', self.__inprogress_response_cb)
            alert.show()
            self.present()
            return False

    def __inprogress_response_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.CANCEL:
            logging.debug('Keep on')
        elif response_id == Gtk.ResponseType.OK:
            logging.debug('Stop downloads and quit')
            self._force_close = True
            downloadmanager.remove_all_downloads()
            self.close()

    def __switch_page_cb(self, tabbed_view, page, page_num):
        browser = page._browser
        status = browser.get_load_status()

        if status in (WebKit.LoadStatus.COMMITTED,
                      WebKit.LoadStatus.FIRST_VISUALLY_NON_EMPTY_LAYOUT):
            self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
        elif status in (WebKit.LoadStatus.PROVISIONAL,
                        WebKit.LoadStatus.FAILED,
                        WebKit.LoadStatus.FINISHED):
            self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))

    def get_document_path(self, async_cb, async_err_cb):
        browser = self._tabbed_view.props.current_browser
        browser.get_source(async_cb, async_err_cb)

    def get_canvas(self):
        return self._tabbed_view
Exemplo n.º 2
0
class Record(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        if Gst.version() == (1, 0, 10, 0):
            return self._incompatible()

        # for fullscreen feature, use local rather than toolkit
        self.props.enable_fullscreen_mode = False

        self._state = None
        Instance(self)

        # the main classes
        self.model = Model(self)
        self.ui_init()

        # CSCL
        self.connect("shared", self._shared_cb)
        if self.get_shared_activity():
            # have you joined or shared this activity yourself?
            if self.get_shared():
                self._joined_cb(self)
            else:
                self.connect("joined", self._joined_cb)

        # Changing to the first toolbar kicks off the rest of the setup
        if self.model.get_cameras():
            self.model.change_mode(constants.MODE_PHOTO)
        else:
            self.model.change_mode(constants.MODE_AUDIO)

        # Start live video pipeline when the video window becomes visible
        def on_defer_cb():
            self.model.set_visible(True)
            self.connect("notify::active", self.__active_cb)
            return False

        def on_event_cb(widget, event):
            if event.state == Gdk.VisibilityState.UNOBSCURED:
                GLib.timeout_add(50, on_defer_cb)
                self._media_view._video.disconnect_by_func(on_event_cb)

        self._media_view._video.add_events(
            Gdk.EventMask.VISIBILITY_NOTIFY_MASK)
        self._media_view._video.connect('visibility-notify-event', on_event_cb)

    def _incompatible(self):
        ''' Display abbreviated activity user interface with alert '''
        toolbox = ToolbarBox()
        stop = StopButton(self)
        toolbox.toolbar.add(stop)
        self.set_toolbar_box(toolbox)

        title = _('Activity not compatible with this system.')
        msg = _('Please downgrade activity and try again.')
        alert = Alert(title=title, msg=msg)
        alert.add_button(0, 'Stop', Icon(icon_name='activity-stop'))
        self.add_alert(alert)

        label = Gtk.Label(_('Uh oh, GStreamer is too old.'))
        self.set_canvas(label)

        alert.connect('response', self.__incompatible_response_cb)
        stop.connect('clicked', self.__incompatible_stop_clicked_cb,
                     alert)

        self.show_all()

    def __incompatible_stop_clicked_cb(self, button, alert):
        self.remove_alert(alert)

    def __incompatible_response_cb(self, alert, response):
        self.remove_alert(alert)
        self.close()

    def read_file(self, path):
        if hasattr(self, 'model'):
            self.model.read_file(path)

    def write_file(self, path):
        if hasattr(self, 'model'):
            self.model.write_file(path)

    def close(self, **kwargs):
        if hasattr(self, 'model'):
            self.model.close()
        activity.Activity.close(self, **kwargs)

    def __active_cb(self, widget, pspec):
        self.model.set_visible(self.props.active)

    def _shared_cb(self, activity):
        self.model.collab.set_activity_shared()

    def _joined_cb(self, activity):
        self.model.collab.joined()

    def ui_init(self):
        self._fullscreen = False
        self._showing_info = False

        # FIXME: if _thumb_tray becomes some kind of button group, we wouldn't
        # have to track which recd is active
        self._active_recd = None

        self.connect('key-press-event', self._key_pressed)

        self._active_toolbar_idx = 0

        toolbar_box = ToolbarBox()
        self._activity_toolbar_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(self._activity_toolbar_button, 0)
        self.set_toolbar_box(toolbar_box)
        self._toolbar = self.get_toolbar_box().toolbar

        tool_group = None
        if self.model.get_cameras():
            self._photo_button = RadioToolButton()
            self._photo_button.props.group = tool_group
            tool_group = self._photo_button
            self._photo_button.props.icon_name = 'camera-external'
            self._photo_button.props.label = _('Photo')
            self._photo_button.props.accelerator = '<ctrl>1'
            self._photo_button.props.tooltip = _(
                'Picture camera mode\n\n'
                'When the record button is pressed,\n'
                'take one picture from the camera.')
            self._photo_button.mode = constants.MODE_PHOTO
            self._photo_button.connect('clicked', self._mode_button_clicked)
            self._toolbar.insert(self._photo_button, -1)

            self._video_button = RadioToolButton()
            self._video_button.props.group = tool_group
            self._video_button.props.icon_name = 'media-video'
            self._video_button.props.accelerator = '<ctrl>2'
            self._video_button.props.label = _('Video')
            self._video_button.props.tooltip = _(
                'Video camera mode\n\n'
                'When the record button is pressed,\n'
                'take photographs many times a second,\n'
                'and record sound using the microphone,\n'
                'until the button is pressed again.')
            self._video_button.mode = constants.MODE_VIDEO
            self._video_button.connect('clicked', self._mode_button_clicked)
            self._toolbar.insert(self._video_button, -1)
        else:
            self._photo_button = None
            self._video_button = None

        self._audio_button = RadioToolButton()
        self._audio_button.props.group = tool_group
        self._audio_button.props.icon_name = 'media-audio'
        self._audio_button.props.accelerator = '<ctrl>3'
        self._audio_button.props.label = _('Audio')
        self._audio_button.props.tooltip = _(
            'Audio recording mode\n\n'
            'When the record button is pressed,\n'
            'take one photograph,\n'
            'and record sound using the microphone,\n'
            'until the button is pressed again.')
        self._audio_button.mode = constants.MODE_AUDIO
        self._audio_button.connect('clicked', self._mode_button_clicked)
        self._toolbar.insert(self._audio_button, -1)

        self._toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self._mirror_btn = ToggleToolButton('mirror-horizontal')
        self._mirror_btn.set_tooltip(_(
            'Mirror view\n\n'
            'Swap left for right, as if looking at a mirror.\n'
            'Does not affect recording.'))
        self._mirror_btn.props.accelerator = '<ctrl>m'
        self._mirror_btn.show()
        self._mirror_btn.connect('toggled', self.__mirror_toggled_cb)
        self._toolbar.insert(self._mirror_btn, -1)

        self._toolbar_controls = RecordControl(self._toolbar)

        if self.model.get_cameras() and len(self.model.get_cameras()) > 1:
            switch_camera_btn = ToolButton('switch-camera')
            switch_camera_btn.set_tooltip(_('Switch camera'))
            switch_camera_btn.show()
            switch_camera_btn.connect('clicked', self.__switch_camera_click_cb)
            self._toolbar.insert(switch_camera_btn, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self._toolbar.insert(separator, -1)
        self._toolbar.insert(StopButton(self), -1)
        self.get_toolbar_box().show_all()

        self._media_view = MediaView()
        self._media_view.connect('media-clicked',
                                 self._media_view_media_clicked)
        self._media_view.connect('pip-clicked', self._media_view_pip_clicked)
        self._media_view.connect('info-clicked', self._media_view_info_clicked)
        self._media_view.connect('fullscreen-clicked',
                                 self._media_view_fullscreen_clicked)
        self._media_view.connect('tags-changed', self._media_view_tags_changed)
        self._media_view.show()

        self._controls_hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        trim_height_shutter_button = 7
        self._controls_hbox.set_size_request(-1, style.GRID_CELL_SIZE +
                                             trim_height_shutter_button)

        self._shutter_button = ShutterButton()
        self._shutter_button.connect("clicked", self._shutter_clicked)
        self._shutter_button.modify_bg(Gtk.StateType.NORMAL, COLOR_BLACK)
        self._controls_hbox.pack_start(self._shutter_button, True, False, 0)

        self._countdown_image = CountdownImage()
        self._controls_hbox.pack_start(self._countdown_image, True, False, 0)

        self._play_button = PlayButton()
        self._play_button.connect('clicked', self._play_pause_clicked)
        self._controls_hbox.pack_start(self._play_button, False, True, 0)

        self._playback_scale = PlaybackScale(self.model)
        self._controls_hbox.pack_start(self._playback_scale, True, True, 0)

        self._progress = ProgressInfo()
        self._controls_hbox.pack_start(self._progress, True, True, 0)

        self._title_label = Gtk.Label()
        self._title_label.set_markup("<b><span foreground='white'>" +
                                     _('Title:') + '</span></b>')
        self._controls_hbox.pack_start(self._title_label, False, True, 0)

        self._title_entry = Gtk.Entry()
        self._title_entry.modify_bg(Gtk.StateType.INSENSITIVE, COLOR_BLACK)
        self._title_entry.connect('changed', self._title_changed)
        self._controls_hbox.pack_start(self._title_entry, expand=True,
                                       fill=True, padding=10)
        self._controls_hbox.show()

        height_tray = 150  # height of tray

        self._thumb_tray = HTray(hexpand=True, height_request=height_tray)
        self._thumb_tray.show()

        height = Gdk.Screen.height() - style.GRID_CELL_SIZE * 2 - \
            height_tray - trim_height_shutter_button
        self._media_view.set_size_request(-1, height)

        self._grid = Gtk.Grid(orientation=Gtk.Orientation.VERTICAL)
        self._media_view.props.hexpand = True
        self._media_view.props.vexpand = True
        for row in [self._media_view, self._controls_hbox, self._thumb_tray]:
            self._grid.add(row)
        self._grid.modify_bg(Gtk.StateType.NORMAL, COLOR_BLACK)
        self._grid.show()
        self.set_canvas(self._grid)

    def set_title_visible(self, visible):
        self._grid.remove(self._controls_hbox)

        if visible:
            self._grid.attach_next_to(self._controls_hbox, self._media_view,
                                      Gtk.PositionType.TOP, 1, 1)
        else:
            self._grid.attach_next_to(self._controls_hbox, self._media_view,
                                      Gtk.PositionType.BOTTOM, 1, 1)

    def __switch_camera_click_cb(self, btn):
        self.model.switch_camera()

    def __mirror_toggled_cb(self, button):
        self.model.set_mirror(button.props.active)

    def serialize(self):
        data = {}

        data['timer'] = self._toolbar_controls.get_timer_idx()
        data['duration'] = self._toolbar_controls.get_duration_idx()
        data['quality'] = self._toolbar_controls.get_quality()

        return data

    def deserialize(self, data):
        self._toolbar_controls.set_timer_idx(data.get('timer', 0))
        self._toolbar_controls.set_duration_idx(data.get('duration', 0))
        self._toolbar_controls.set_quality(data.get('quality', 0))

    def _key_pressed(self, widget, event):
        key = event.keyval
        ctrl = event.state & Gdk.ModifierType.CONTROL_MASK

        # while activity toolbar is visible, only escape key is taken
        if self._activity_toolbar_button.is_expanded():
            if key == Gdk.KEY_Escape:
                self._activity_toolbar_button.set_expanded(False)
                return True

            return False

        # while title is focused, only escape key is taken
        if self._title_entry.is_focus():
            if key == Gdk.KEY_Escape:
                self.model.set_state(constants.STATE_READY)

            return False

        # while info tags are focused, only escape key is taken
        if self._media_view.info_view.textview.is_focus():
            if key == Gdk.KEY_Escape:
                self.model.set_state(constants.STATE_READY)

            return False

        if ctrl and key == Gdk.KEY_f:
            self._toggle_fullscreen()
            return True

        if ctrl and key == Gdk.KEY_s:
            self.model.glive.stop()
            return True

        if ctrl and key == Gdk.KEY_p:
            self.model.glive.play()
            return True

        if (ctrl and key == Gdk.KEY_space) or \
           (ctrl and key == Gdk.KEY_r) or \
           key == Gdk.KEY_KP_Page_Up:  # game key O

            if self._shutter_button.props.visible:
                if self._shutter_button.props.sensitive:
                    self._shutter_button.clicked()
            else:  # return to live mode
                self.model.set_state(constants.STATE_READY)
            return True

        if key == Gdk.KEY_space and self._active_recd:
            if self._active_recd.type in (constants.TYPE_VIDEO,
                                          constants.TYPE_AUDIO):
                self.model.play_pause()
                return True

        # if viewing media, return to live mode
        if key == Gdk.KEY_Escape and self._active_recd:
            self.model.set_state(constants.STATE_READY)
            return True

        if self.model.ui_frozen():
            return True

        if ctrl and key == Gdk.KEY_c:
            self._copy_to_clipboard(self._active_recd)
            return True

        if key == Gdk.KEY_i and self._active_recd:
            self._toggle_info()
            return True

        if key == Gdk.KEY_Escape and self._fullscreen:
            self._toggle_fullscreen()
            return True

        return False

    def _play_pause_clicked(self, widget):
        self.model.play_pause()

    def set_mode(self, mode):
        self._toolbar_controls.set_mode(mode)

    # can be called from GStreamer thread, so must not do any GTK+ stuff
    def set_glive_sink(self, sink):
        return self._media_view.set_video_sink(sink)

    # can be called from GStreamer thread, so must not do any GTK+ stuff
    def set_gplay_sink(self, sink):
        return self._media_view.set_video2_sink(sink)

    def get_selected_quality(self):
        return self._toolbar_controls.get_quality()

    def get_selected_timer(self):
        return self._toolbar_controls.get_timer()

    def get_selected_duration(self):
        return self._toolbar_controls.get_duration() * 60  # convert to secs

    def set_progress(self, value, text):
        self._progress.set_progress(value)
        self._progress.set_text(text)

    def set_countdown(self, value):
        if value == 0:
            self._shutter_button.show()
            self._countdown_image.hide()
            return

        self._shutter_button.hide()
        self._countdown_image.show()
        self._countdown_image.set_value(value)

    def _title_changed(self, widget):
        self._active_recd.setTitle(self._title_entry.get_text())

    def _media_view_media_clicked(self, widget):
        if self._play_button.props.visible and \
           self._play_button.props.sensitive:
            self._play_button.clicked()

    def _media_view_pip_clicked(self, widget):
        # clicking on the PIP always returns to live mode
        self.model.set_state(constants.STATE_READY)

    def _media_view_info_clicked(self, widget):
        self._toggle_info()

    def _toggle_info(self):
        recd = self._active_recd
        if not recd:
            return

        if self._showing_info:
            self._show_recd(recd, play=False)
            return

        self._showing_info = True
        still_modes = (constants.MODE_PHOTO, constants.MODE_AUDIO)
        if self.model.get_mode() in still_modes:
            func = self._media_view.show_info_photo
        else:
            func = self._media_view.show_info_video

        self._play_button.hide()
        self._progress.hide()
        self._playback_scale.hide()
        self._title_entry.set_text(recd.title)
        self._title_entry.show()
        self._title_label.show()
        self.set_title_visible(True)

        func(recd.recorderName, recd.colorStroke, recd.colorFill,
             utils.getDateString(recd.time), recd.tags)

    def _media_view_fullscreen_clicked(self, widget):
        # logger.debug('_media_view_fullscreen_clicked')
        self._toggle_fullscreen()

    def _media_view_tags_changed(self, widget, tbuffer):
        text = tbuffer.get_text(tbuffer.get_start_iter(),
                                tbuffer.get_end_iter(), True)
        self._active_recd.setTags(text)

    def _toggle_fullscreen(self):
        # logger.debug('_toggle_fullscreen')
        self._fullscreen = not self._fullscreen

        if not self._active_recd:
            self.model.glive.stop()

        if self._fullscreen:
            self.get_toolbar_box().hide()
            self._thumb_tray.hide()
            if self._active_recd:
                self._controls_hbox.hide()
        else:
            self.get_toolbar_box().show()
            self._thumb_tray.show()
            self._controls_hbox.show()

        self._media_view.set_fullscreen(self._fullscreen)

        if self._active_recd:
            return

        if self.model.get_state() == constants.STATE_RECORDING:
            return

        # hack, reason unknown
        # problem: call to self.mode.glive.play() does not show live view
        # solution: defer until after VideoBox resize is complete

        self._timer_hid = None

        def on_timer_cb():
            self.model.glive.play()
            self._timer_hid = None
            return False

        self._timer_hid = GLib.timeout_add(1000, on_timer_cb)

        def on_defer_cb():
            self.model.glive.play()
            if self._timer_hid:
                GLib.source_remove(self._timer_hid)
                self._timer_hid = None
            return False

        def on_event_cb(widget, event):
            if event.state == Gdk.VisibilityState.UNOBSCURED:
                GLib.timeout_add(30, on_defer_cb)
                self._media_view._video.disconnect_by_func(on_event_cb)

        self._media_view._video.add_events(
            Gdk.EventMask.VISIBILITY_NOTIFY_MASK)
        self._media_view._video.connect('visibility-notify-event', on_event_cb)

        # FIXME: fullscreen toggle during video recording gives black
        # window, TODO: do the same as above for the video recording
        # pipeline when it is active

    def _mode_button_clicked(self, button):
        self.model.change_mode(button.mode)

    def _shutter_clicked(self, arg):
        self.model.do_shutter()

    def set_shutter_sensitive(self, value):
        self._shutter_button.set_sensitive(value)

    def set_state(self, state):
        radio_state = (state == constants.STATE_READY)
        for item in (self._photo_button,
                     self._audio_button,
                     self._video_button):
            if item:
                item.set_sensitive(radio_state)

        self._showing_info = False
        if state == constants.STATE_READY:
            if self._state == constants.STATE_PROCESSING:
                self.unbusy()
            self._active_recd = None
            self._mirror_btn.props.sensitive = True
            self._title_entry.hide()
            self._title_label.hide()
            self.set_title_visible(False)
            self._play_button.hide()
            self._playback_scale.hide()
            self._progress.hide()
            self._controls_hbox.set_child_packing(self._shutter_button,
                                                  expand=True, fill=False,
                                                  padding=0,
                                                  pack_type=Gtk.PackType.START)
            self._shutter_button.set_normal()
            self._shutter_button.set_sensitive(True)
            self._shutter_button.show()
            self._media_view.show_live()
        elif state == constants.STATE_RECORDING:
            self._mirror_btn.props.sensitive = False
            self._shutter_button.set_recording()
            self._controls_hbox.set_child_packing(self._shutter_button,
                                                  expand=False, fill=False,
                                                  padding=0,
                                                  pack_type=Gtk.PackType.START)
            self._progress.show()
        elif state == constants.STATE_PROCESSING:
            self.busy()
            self._shutter_button.hide()
            self._progress.show()
        elif state == constants.STATE_DOWNLOADING:
            self._shutter_button.hide()
            self._progress.show()
        self._state = state

    def set_paused(self, value):
        if value:
            self._play_button.set_play()
        else:
            self._play_button.set_pause()

    def _thumbnail_clicked(self, button, recd):
        if self.model.ui_frozen():
            return

        self.model.abort_countdown()
        self.model.glive.stop()
        self._mirror_btn.props.sensitive = False
        self._active_recd = recd
        self._show_recd(recd)

    def add_thumbnail(self, recd):
        button = RecdButton(recd)
        clicked_handler = button.connect("clicked",
                                         self._thumbnail_clicked, recd)
        remove_handler = button.connect("remove-requested",
                                        self._remove_recd)
        clipboard_handler = button.connect("copy-clipboard-requested",
                                           self._thumbnail_copy_clipboard)
        button.handler_ids = (clicked_handler, remove_handler,
                              clipboard_handler)
        button.show()
        self._thumb_tray.add_item(button)
        self._thumb_tray.scroll_to_item(button)
        # FIXME: possible toolkit bug; scroll_to_item is ineffective,
        # only noticed when the tray is full

    def _copy_to_clipboard(self, recd):
        if recd is None:
            return
        if not recd.isClipboardCopyable():
            return

        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        clipboard.set_image(recd.getCopyClipboardPixbuf())

    def _thumbnail_copy_clipboard(self, recdbutton):
        self._copy_to_clipboard(recdbutton.get_recd())

    def _remove_recd(self, recdbutton):
        recd = recdbutton.get_recd()
        self.model.delete_recd(recd)
        if self._active_recd == recd:
            self.model.set_state(constants.STATE_READY)

        self._remove_thumbnail(recdbutton)

    def _remove_thumbnail(self, recdbutton):
        for handler in recdbutton.handler_ids:
            recdbutton.disconnect(handler)

        self._thumb_tray.remove_item(recdbutton)
        recdbutton.cleanup()

    def show_still(self, pixbuf):
        self._media_view.show_still(pixbuf)

    def _show_photo(self, recd):
        path = self._get_photo_path(recd)
        self._media_view.show_photo(path)
        self._title_entry.set_text(recd.title)
        self._title_entry.show()
        self._title_label.show()
        self.set_title_visible(True)
        self._shutter_button.hide()
        self._progress.hide()

    def _show_audio(self, recd, play):
        self._progress.hide()
        self._shutter_button.hide()
        self._title_entry.hide()
        self._title_label.hide()
        self.set_title_visible(False)
        self._play_button.show()
        self._playback_scale.show()
        path = recd.getAudioImageFilepath()
        self._media_view.show_photo(path)
        if play:
            self.model.play_audio(recd)

    def _show_video(self, recd, play):
        self._progress.hide()
        self._shutter_button.hide()
        self._title_entry.hide()
        self._title_label.hide()
        self.set_title_visible(False)
        self._play_button.show()
        self._playback_scale.show()
        self._media_view.show_video()
        if play:
            self.model.play_video(recd)

    def set_playback_scale(self, value):
        self._playback_scale.set_value(value)

    def _get_photo_path(self, recd):
        # FIXME should live (partially) in recd?

        # downloading = self.ca.requestMeshDownload(recd)
        # self.MESHING = downloading

        if True:  # not downloading:
            # self.progressWindow.updateProgress(0, "")
            return recd.getMediaFilepath()

        # maybe it is not downloaded from the mesh yet...
        # but we can show the low res thumb in the interim
        return recd.getThumbFilepath()

    def _show_recd(self, recd, play=True):
        self._showing_info = False

        if recd.buddy and not recd.downloadedFromBuddy:
            self.model.request_download(recd)
        elif recd.type == constants.TYPE_PHOTO:
            self._show_photo(recd)
        elif recd.type == constants.TYPE_AUDIO:
            self._show_audio(recd, play)
        elif recd.type == constants.TYPE_VIDEO:
            self._show_video(recd, play)

    def remote_recd_available(self, recd):
        self.model.set_state(constants.STATE_INVISIBLE)
        if recd == self._active_recd:
            self._show_recd(recd)

    def update_download_progress(self, recd):
        if recd != self._active_recd:
            return

        if not recd.meshDownloading:
            msg = _('Download failed.')
        elif recd.meshDownloadingProgress:
            msg = _('Downloading...')
        else:
            msg = _('Requesting...')

        self.set_progress(recd.meshDownlodingPercent, msg)
Exemplo n.º 3
0
import common

test = common.Test()

vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

tray = HTray()
vbox.pack_start(tray, False, False, 0)
tray.show()

theme_icons = Gtk.IconTheme.get_default().list_icons(context=None)

for i in range(0, 100):
    button = TrayButton(icon_name=theme_icons[i])
    tray.add_item(button)
    button.show()

tray = HTray()
vbox.pack_start(tray, False, False, 0)
tray.show()

for i in range(0, 10):
    icon = TrayIcon(icon_name=theme_icons[i])
    tray.add_item(icon)
    icon.show()

hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

tray = VTray()
hbox.pack_start(tray, False, False, 0)
Exemplo n.º 4
0
box.pack_start(toolbar, False, False, 0)
toolbar.show()

for i in range(0, 5):
    button = ToolButton(icon_name=theme_icons[i])
    button.set_tooltip('Icon %d' % i)
    toolbar.insert(button, -1)
    button.show()

content = Gtk.Label()
box.pack_start(content, True, True, 0)
content.show()

tray = HTray()
box.pack_start(tray, False, False, 0)
tray.show()

for i in range(0, 30):
    button = TrayButton(icon_name=theme_icons[i])
    button.set_tooltip('Icon %d' % i)
    tray.add_item(button)
    button.show()

test.pack_start(box, True, True, 0)
box.show()

test.show()

if __name__ == '__main__':
    common.main(test)