Example #1
0
class MazeActivity(activity.Activity):
    def __init__(self, handle):
        """Set up the Maze activity."""
        activity.Activity.__init__(self, handle)
        self._busy_count = 0
        self._unbusy_idle_sid = None

        self.build_toolbar()

        self.pservice = PresenceService()
        self.owner = self.pservice.get_owner()

        state = None
        if 'state' in self.metadata:
            state = json.loads(self.metadata['state'])
        self.game = game.MazeGame(self, self.owner, state)
        self.set_canvas(self.game)
        self.game.show()
        self.connect("key_press_event", self.game.key_press_cb)

        self.text_channel = None
        self.my_key = profile.get_pubkey()
        self._alert = None

        if self.shared_activity:
            # we are joining the activity
            self._add_alert(_('Joining a maze'), _('Connecting...'))
            self.connect('joined', self._joined_cb)
            if self.get_shared():
                # we have already joined
                self._joined_cb()
        else:
            # we are creating the activity
            self.connect('shared', self._shared_cb)

    def busy(self):
        if self._busy_count == 0:
            if self._unbusy_idle_sid is not None:
                GLib.source_remove(self._unbusy_idle_sid)
                self._unbusy_idle_sid = None
            self._old_cursor = self.get_window().get_cursor()
            self._set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
        self._busy_count += 1

    def unbusy(self):
        self._unbusy_idle_sid = GLib.idle_add(self._unbusy_idle_cb)

    def _unbusy_idle_cb(self):
        self._unbusy_idle_sid = None
        self._busy_count -= 1
        if self._busy_count == 0:
            self._set_cursor(self._old_cursor)

    def _set_cursor(self, cursor):
        self.get_window().set_cursor(cursor)
        Gdk.flush()

    def build_toolbar(self):
        """Build our Activity toolbar for the Sugar system."""

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        easier_button = ToolButton('create-easier')
        easier_button.set_tooltip(_('Easier level'))
        easier_button.connect('clicked', self._easier_button_cb)
        toolbar_box.toolbar.insert(easier_button, -1)

        harder_button = ToolButton('create-harder')
        harder_button.set_tooltip(_('Harder level'))
        harder_button.connect('clicked', self._harder_button_cb)
        toolbar_box.toolbar.insert(harder_button, -1)

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        self.show_trail_button = ToggleToolButton('show-trail')
        self.show_trail_button.set_tooltip(_('Show trail'))
        self.show_trail_button.set_active(True)
        self.show_trail_button.connect('toggled', self._toggled_show_trail_cb)
        toolbar_box.toolbar.insert(self.show_trail_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        return toolbar_box

    def _easier_button_cb(self, button):
        self.game.easier()

    def _harder_button_cb(self, button):
        self.game.harder()

    def _toggled_show_trail_cb(self, button):
        if self.game.set_show_trail(button.get_active()):
            self.broadcast_msg('show_trail:%s' % str(button.get_active()))

    def _shared_cb(self, activity):
        logging.debug('Maze was shared')
        self._add_alert(_('Sharing'), _('This maze is shared.'))
        self._setup()

    def _joined_cb(self, activity):
        """Joined a shared activity."""
        if not self.shared_activity:
            return
        logging.debug('Joined a shared chat')
        for buddy in self.shared_activity.get_joined_buddies():
            self._buddy_already_exists(buddy)
        self._setup()
        # request maze data
        self.broadcast_msg('req_maze')

    def _setup(self):
        self.text_channel = TextChannelWrapper(
            self.shared_activity.telepathy_text_chan,
            self.shared_activity.telepathy_conn, self.pservice)
        self.text_channel.set_received_callback(self._received_cb)
        self.shared_activity.connect('buddy-joined', self._buddy_joined_cb)
        self.shared_activity.connect('buddy-left', self._buddy_left_cb)

    def _received_cb(self, buddy, text):
        if buddy == self.owner:
            return
        self.game.msg_received(buddy, text)

    def _add_alert(self, title, text=None):
        self.grab_focus()
        self._alert = ErrorAlert()
        self._alert.props.title = title
        self._alert.props.msg = text
        self.add_alert(self._alert)
        self._alert.connect('response', self._alert_cancel_cb)
        self._alert.show()

    def _alert_cancel_cb(self, alert, response_id):
        self.remove_alert(alert)
        self._alert = None

    def update_alert(self, title, text=None):
        if self._alert is not None:
            self._alert.props.title = title
            self._alert.props.msg = text

    def show_accelerator_alert(self):
        self.grab_focus()
        self._alert = NotifyAlert()
        self._alert.props.title = _('Tablet mode detected.')
        self._alert.props.msg = _('Hold your XO flat and tilt to play!')
        self.add_alert(self._alert)
        self._alert.connect('response', self._alert_cancel_cb)
        self._alert.show()

    def _buddy_joined_cb(self, activity, buddy):
        """Show a buddy who joined"""
        logging.debug('buddy joined')
        if buddy == self.owner:
            logging.debug('its me, exit!')
            return
        self.game.buddy_joined(buddy)

    def _buddy_left_cb(self, activity, buddy):
        self.game.buddy_left(buddy)

    def _buddy_already_exists(self, buddy):
        """Show a buddy already in the chat."""
        if buddy == self.owner:
            return
        self.game.buddy_joined(buddy)

    def broadcast_msg(self, message):
        if self.text_channel:
            # FIXME: can't identify the sender at the other end,
            # add the pubkey to the text message
            self.text_channel.send('%s|%s' % (self.my_key, message))

    def write_file(self, file_path):
        logging.debug('Saving the state of the game...')
        data = {
            'seed': self.game.maze.seed,
            'width': self.game.maze.width,
            'height': self.game.maze.height,
            'finish_time': self.game.finish_time
        }
        logging.debug('Saving data: %s', data)
        self.metadata['state'] = json.dumps(data)

    def can_close(self):
        self.game.close_finish_window()
        return True

    def read_file(self, file_path):
        pass
class MiniChat(Activity):
    def __init__(self, handle):
        Activity.__init__(self, handle)

        root = self.make_root()
        self.set_canvas(root)
        root.show_all()
        self.entry.grab_focus()

        toolbox = ActivityToolbox(self)
        activity_toolbar = toolbox.get_activity_toolbar()
        activity_toolbar.keep.props.visible = False
        self.set_toolbox(toolbox)
        toolbox.show()

        self.owner = self._pservice.get_owner()
        # Auto vs manual scrolling:
        self._scroll_auto = True
        self._scroll_value = 0.0
        # Track last message, to combine several messages:
        self._last_msg = None
        self._last_msg_sender = None
        self.text_channel = None

        if self._shared_activity:
            # we are joining the activity
            self.connect('joined', self._joined_cb)
            if self.get_shared():
                # we have already joined
                self._joined_cb()
        else:
            # we are creating the activity
            if not self.metadata or self.metadata.get(
                    'share-scope', SCOPE_PRIVATE) == SCOPE_PRIVATE:
                # if we are in private session
                self._alert(_('Off-line'), _('Share, or invite someone.'))
            self.connect('shared', self._shared_cb)

    def _shared_cb(self, activity):
        logger.debug('Chat was shared')
        self._setup()

    def _joined_cb(self, activity):
        """Joined a shared activity."""
        if not self._shared_activity:
            return
        logger.debug('Joined a shared chat')
        for buddy in self._shared_activity.get_joined_buddies():
            self._buddy_already_exists(buddy)
        self._setup()

    def _setup(self):
        self.text_channel = TextChannelWrapper(
            self._shared_activity.telepathy_text_chan,
            self._shared_activity.telepathy_conn)
        self.text_channel.set_received_callback(self._received_cb)
        self._alert(_('On-line'), _('Connected'))
        self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
        self._shared_activity.connect('buddy-left', self._buddy_left_cb)
        self.entry.set_sensitive(True)
        self.entry.grab_focus()

    def _received_cb(self, buddy, text):
        """Show message that was received."""
        if buddy:
            nick = buddy.props.nick
        else:
            nick = '???'
        logger.debug('Received message from %s: %s', nick, text)
        self.add_text(buddy, text)

    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 _buddy_joined_cb(self, activity, buddy):
        """Show a buddy who joined"""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.props.nick
        else:
            nick = '???'
        self.add_text(buddy,
                      buddy.props.nick + ' ' + _('joined the chat'),
                      status_message=True)

    def _buddy_left_cb(self, activity, buddy):
        """Show a buddy who joined"""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.props.nick
        else:
            nick = '???'
        self.add_text(buddy,
                      buddy.props.nick + ' ' + _('left the chat'),
                      status_message=True)

    def _buddy_already_exists(self, buddy):
        """Show a buddy already in the chat."""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.props.nick
        else:
            nick = '???'
        self.add_text(buddy,
                      buddy.props.nick + ' ' + _('is here'),
                      status_message=True)

    def make_root(self):
        conversation = hippo.CanvasBox(spacing=0,
                                       background_color=COLOR_WHITE.get_int())
        self.conversation = conversation

        entry = gtk.Entry()
        entry.modify_bg(gtk.STATE_INSENSITIVE, COLOR_WHITE.get_gdk_color())
        entry.modify_base(gtk.STATE_INSENSITIVE, COLOR_WHITE.get_gdk_color())
        entry.set_sensitive(False)
        entry.connect('activate', self.entry_activate_cb)
        self.entry = entry

        hbox = gtk.HBox()
        hbox.add(entry)

        sw = hippo.CanvasScrollbars()
        sw.set_policy(hippo.ORIENTATION_HORIZONTAL, hippo.SCROLLBAR_NEVER)
        sw.set_root(conversation)
        self.scrolled_window = sw

        vadj = self.scrolled_window.props.widget.get_vadjustment()
        vadj.connect('changed', self.rescroll)
        vadj.connect('value-changed', self.scroll_value_changed_cb)

        canvas = hippo.Canvas()
        canvas.set_root(sw)

        box = gtk.VBox(homogeneous=False)
        box.pack_start(hbox, expand=False)
        box.pack_start(canvas)

        return box

    def rescroll(self, adj, scroll=None):
        """Scroll the chat window to the bottom"""
        if self._scroll_auto:
            adj.set_value(adj.upper - adj.page_size)
            self._scroll_value = adj.get_value()

    def scroll_value_changed_cb(self, adj, scroll=None):
        """Turn auto scrolling on or off.
        
        If the user scrolled up, turn it off.
        If the user scrolled to the bottom, turn it back on.
        """
        if adj.get_value() < self._scroll_value:
            self._scroll_auto = False
        elif adj.get_value() == adj.upper - adj.page_size:
            self._scroll_auto = True

    def add_text(self, buddy, text, status_message=False):
        """Display text on screen, with name and colors.

        buddy -- buddy object
        text -- string, what the buddy said
        status_message -- boolean
            False: show what buddy said
            True: show what buddy did

        hippo layout:
        .------------- rb ---------------.
        | +name_vbox+ +----msg_vbox----+ |
        | |         | |                | |
        | | nick:   | | +--msg_hbox--+ | |
        | |         | | | text       | | |
        | +---------+ | +------------+ | |
        |             |                | |
        |             | +--msg_hbox--+ | |
        |             | | text       | | |
        |             | +------------+ | |
        |             +----------------+ |
        `--------------------------------'
        """
        if buddy:
            nick = buddy.props.nick
            color = buddy.props.color
            try:
                color_stroke_html, color_fill_html = color.split(',')
            except ValueError:
                color_stroke_html, color_fill_html = ('#000000', '#888888')
            # Select text color based on fill color:
            color_fill_rgba = Color(color_fill_html).get_rgba()
            color_fill_gray = (color_fill_rgba[0] + color_fill_rgba[1] +
                               color_fill_rgba[2]) / 3
            color_stroke = Color(color_stroke_html).get_int()
            color_fill = Color(color_fill_html).get_int()
            if color_fill_gray < 0.5:
                text_color = COLOR_WHITE.get_int()
            else:
                text_color = COLOR_BLACK.get_int()
        else:
            nick = '???'  # XXX: should be '' but leave for debugging
            color_stroke = COLOR_BLACK.get_int()
            color_fill = COLOR_WHITE.get_int()
            text_color = COLOR_BLACK.get_int()
            color = '#000000,#FFFFFF'

        # Check for Right-To-Left languages:
        if pango.find_base_dir(nick, -1) == pango.DIRECTION_RTL:
            lang_rtl = True
        else:
            lang_rtl = False

        # Check if new message box or add text to previous:
        new_msg = True
        if self._last_msg_sender:
            if not status_message:
                if buddy == self._last_msg_sender:
                    # Add text to previous message
                    new_msg = False

        if not new_msg:
            rb = self._last_msg
            msg_vbox = rb.get_children()[1]
            msg_hbox = hippo.CanvasBox(
                orientation=hippo.ORIENTATION_HORIZONTAL)
            msg_vbox.append(msg_hbox)
        else:
            rb = CanvasRoundBox(background_color=color_fill,
                                border_color=color_stroke,
                                padding=4)
            rb.props.border_color = color_stroke  # Bug #3742
            self._last_msg = rb
            self._last_msg_sender = buddy
            if not status_message:
                name = hippo.CanvasText(text=nick + ':   ',
                                        color=text_color,
                                        font_desc=FONT_BOLD.get_pango_desc())
                name_vbox = hippo.CanvasBox(
                    orientation=hippo.ORIENTATION_VERTICAL)
                name_vbox.append(name)
                rb.append(name_vbox)
            msg_vbox = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL)
            rb.append(msg_vbox)
            msg_hbox = hippo.CanvasBox(
                orientation=hippo.ORIENTATION_HORIZONTAL)
            msg_vbox.append(msg_hbox)

        if status_message:
            self._last_msg_sender = None

        if text:
            message = hippo.CanvasText(text=text,
                                       size_mode=hippo.CANVAS_SIZE_WRAP_WORD,
                                       color=text_color,
                                       font_desc=FONT_NORMAL.get_pango_desc(),
                                       xalign=hippo.ALIGNMENT_START)
            msg_hbox.append(message)

        # Order of boxes for RTL languages:
        if lang_rtl:
            msg_hbox.reverse()
            if new_msg:
                rb.reverse()

        if new_msg:
            box = hippo.CanvasBox(padding=2)
            box.append(rb)
            self.conversation.append(box)

    def entry_activate_cb(self, entry):
        text = entry.props.text
        logger.debug('Entry: %s' % text)
        if text:
            self.add_text(self.owner, text)
            entry.props.text = ''
            if self.text_channel:
                self.text_channel.send(text)
            else:
                logger.debug('Tried to send message but text channel '
                             'not connected.')
Example #3
0
class MazeActivity(activity.Activity):

    def __init__(self, handle):
        """Set up the Maze activity."""
        activity.Activity.__init__(self, handle)
        self._busy_count = 0
        self._unbusy_idle_sid = None

        self.build_toolbar()

        self.pservice = PresenceService()
        self.owner = self.pservice.get_owner()

        state = None
        if 'state' in self.metadata:
            state = json.loads(self.metadata['state'])
        self.game = game.MazeGame(self, self.owner, state)
        self.set_canvas(self.game)
        self.game.show()
        self.connect("key_press_event", self.game.key_press_cb)

        self.text_channel = None
        self.my_key = profile.get_pubkey()
        self._alert = None

        if self.shared_activity:
            # we are joining the activity
            self._add_alert(_('Joining a maze'), _('Connecting...'))
            self.connect('joined', self._joined_cb)
            if self.get_shared():
                # we have already joined
                self._joined_cb()
        else:
            # we are creating the activity
            self.connect('shared', self._shared_cb)

    def busy(self):
        if self._busy_count == 0:
            if self._unbusy_idle_sid is not None:
                GLib.source_remove(self._unbusy_idle_sid)
                self._unbusy_idle_sid = None
            self._old_cursor = self.get_window().get_cursor()
            self._set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
        self._busy_count += 1

    def unbusy(self):
        self._unbusy_idle_sid = GLib.idle_add(self._unbusy_idle_cb)

    def _unbusy_idle_cb(self):
        self._unbusy_idle_sid = None
        self._busy_count -= 1
        if self._busy_count == 0:
            self._set_cursor(self._old_cursor)

    def _set_cursor(self, cursor):
        self.get_window().set_cursor(cursor)
        Gdk.flush()

    def build_toolbar(self):
        """Build our Activity toolbar for the Sugar system."""

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        easier_button = ToolButton('create-easier')
        easier_button.set_tooltip(_('Easier level'))
        easier_button.connect('clicked', self._easier_button_cb)
        toolbar_box.toolbar.insert(easier_button, -1)

        harder_button = ToolButton('create-harder')
        harder_button.set_tooltip(_('Harder level'))
        harder_button.connect('clicked', self._harder_button_cb)
        toolbar_box.toolbar.insert(harder_button, -1)

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        self.show_trail_button = ToggleToolButton('show-trail')
        self.show_trail_button.set_tooltip(_('Show trail'))
        self.show_trail_button.set_active(True)
        self.show_trail_button.connect('toggled', self._toggled_show_trail_cb)
        toolbar_box.toolbar.insert(self.show_trail_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        return toolbar_box

    def _easier_button_cb(self, button):
        self.game.easier()

    def _harder_button_cb(self, button):
        self.game.harder()

    def _toggled_show_trail_cb(self, button):
        if self.game.set_show_trail(button.get_active()):
            self.broadcast_msg('show_trail:%s' % str(button.get_active()))

    def _shared_cb(self, activity):
        logging.debug('Maze was shared')
        self._add_alert(_('Sharing'), _('This maze is shared.'))
        self._setup()

    def _joined_cb(self, activity):
        """Joined a shared activity."""
        if not self.shared_activity:
            return
        logging.debug('Joined a shared chat')
        for buddy in self.shared_activity.get_joined_buddies():
            self._buddy_already_exists(buddy)
        self._setup()
        # request maze data
        self.broadcast_msg('req_maze')

    def _setup(self):
        self.text_channel = TextChannelWrapper(
            self.shared_activity.telepathy_text_chan,
            self.shared_activity.telepathy_conn, self.pservice)
        self.text_channel.set_received_callback(self._received_cb)
        self.shared_activity.connect('buddy-joined', self._buddy_joined_cb)
        self.shared_activity.connect('buddy-left', self._buddy_left_cb)

    def _received_cb(self, buddy, text):
        if buddy == self.owner:
            return
        self.game.msg_received(buddy, text)

    def _add_alert(self, title, text=None):
        self.grab_focus()
        self._alert = ErrorAlert()
        self._alert.props.title = title
        self._alert.props.msg = text
        self.add_alert(self._alert)
        self._alert.connect('response', self._alert_cancel_cb)
        self._alert.show()

    def _alert_cancel_cb(self, alert, response_id):
        self.remove_alert(alert)
        self._alert = None

    def update_alert(self, title, text=None):
        if self._alert is not None:
            self._alert.props.title = title
            self._alert.props.msg = text

    def show_accelerator_alert(self):
        self.grab_focus()
        self._alert = NotifyAlert()
        self._alert.props.title = _('Tablet mode detected.')
        self._alert.props.msg = _('Hold your XO flat and tilt to play!')
        self.add_alert(self._alert)
        self._alert.connect('response', self._alert_cancel_cb)
        self._alert.show()

    def _buddy_joined_cb(self, activity, buddy):
        """Show a buddy who joined"""
        logging.debug('buddy joined')
        if buddy == self.owner:
            logging.debug('its me, exit!')
            return
        self.game.buddy_joined(buddy)

    def _buddy_left_cb(self, activity, buddy):
        self.game.buddy_left(buddy)

    def _buddy_already_exists(self, buddy):
        """Show a buddy already in the chat."""
        if buddy == self.owner:
            return
        self.game.buddy_joined(buddy)

    def broadcast_msg(self, message):
        if self.text_channel:
            # FIXME: can't identify the sender at the other end,
            # add the pubkey to the text message
            self.text_channel.send('%s|%s' % (self.my_key, message))

    def write_file(self, file_path):
        logging.debug('Saving the state of the game...')
        data = {'seed': self.game.maze.seed,
                'width': self.game.maze.width,
                'height': self.game.maze.height,
                'finish_time': self.game.finish_time}
        logging.debug('Saving data: %s', data)
        self.metadata['state'] = json.dumps(data)

    def can_close(self):
        self.game.close_finish_window()
        return True

    def read_file(self, file_path):
        pass
Example #4
0
class MiniChat(Activity):
    def __init__(self, handle):
        Activity.__init__(self, handle)

        toolbox = ActivityToolbar(self)

        stop_button = StopButton(self)
        stop_button.show()
        toolbox.insert(stop_button, -1)

        self.set_toolbar_box(toolbox)
        toolbox.show()

        self.scroller = Gtk.ScrolledWindow()
        self.scroller.set_vexpand(True)

        root = self.make_root()
        self.set_canvas(root)
        root.show_all()
        self.entry.grab_focus()

        self.pservice = PresenceService()
        self.owner = self.pservice.get_owner()

        # Track last message, to combine several messages:
        self._last_msg = None
        self._last_msg_sender = None
        self.text_channel = None

        if self.shared_activity:
            # we are joining the activity
            self.connect('joined', self._joined_cb)
            if self.get_shared():
                # we have already joined
                self._joined_cb()
        else:
            # we are creating the activity
            if not self.metadata or self.metadata.get('share-scope',
                    SCOPE_PRIVATE) == SCOPE_PRIVATE:
                # if we are in private session
                self._alert(_('Off-line'), _('Share, or invite someone.'))
            self.connect('shared', self._shared_cb)

    def _shared_cb(self, activity):
        logger.debug('Chat was shared')
        self._setup()

    def _joined_cb(self, activity):
        """Joined a shared activity."""
        if not self.shared_activity:
            return
        logger.debug('Joined a shared chat')
        for buddy in self.shared_activity.get_joined_buddies():
            self._buddy_already_exists(buddy)
        self._setup()

    def _setup(self):
        self.text_channel = TextChannelWrapper(
            self.shared_activity.telepathy_text_chan,
            self.shared_activity.telepathy_conn)
        self.text_channel.set_received_callback(self._received_cb)
        self._alert(_('On-line'), _('Connected'))
        self.shared_activity.connect('buddy-joined', self._buddy_joined_cb)
        self.shared_activity.connect('buddy-left', self._buddy_left_cb)
        self.entry.set_sensitive(True)
        self.entry.grab_focus()

    def _received_cb(self, buddy, text):
        """Show message that was received."""
        if buddy:
            nick = buddy.nick
        else:
            nick = '???'
        logger.debug('Received message from %s: %s', nick, text)
        self.add_text(buddy, text)

    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 _buddy_joined_cb (self, activity, buddy):
        """Show a buddy who joined"""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.nick
        else:
            nick = '???'
        self.add_text(buddy, buddy.nick+' '+_('joined the chat'),
            status_message=True)

    def _buddy_left_cb (self, activity, buddy):
        """Show a buddy who joined"""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.nick
        else:
            nick = '???'
        self.add_text(buddy, buddy.nick+' '+_('left the chat'),
            status_message=True)

    def _buddy_already_exists(self, buddy):
        """Show a buddy already in the chat."""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.nick
        else:
            nick = '???'
        self.add_text(buddy, buddy.nick+' '+_('is here'),
            status_message=True)

    def make_root(self):
        vbox = Gtk.VBox()

        self.conversation = Gtk.VBox()
        self.conversation.show_all()
        self.scroller.add_with_viewport(self.conversation)
        self.scroller.override_background_color(Gtk.StateType.NORMAL,
                                                Gdk.RGBA(*COLOR_WHITE.get_rgba()))
        self.entry = Gtk.Entry()
        self.entry.modify_bg(Gtk.StateType.INSENSITIVE,
                             COLOR_WHITE.get_gdk_color())
        self.entry.modify_base(Gtk.StateType.INSENSITIVE,
                               COLOR_WHITE.get_gdk_color())
        self.entry.set_sensitive(False)

        setattr(self.entry, "nick", "???")
        self.entry.connect('activate', self.entry_activate_cb)

        vbox.pack_start(self.entry, False, False, 0)
        vbox.pack_end(self.scroller, True, True, 0)

        vbox.show()

        box = Gtk.VBox(homogeneous=False)
        box.pack_end(vbox, False, True, 0)
        box.show_all()

        return box

    def add_text(self, buddy, text, status_message=False):
        """Display text on screen, with name and colors.

        buddy -- buddy object
        text -- string, what the buddy said
        status_message -- boolean
            False: show what buddy said
            True: show what buddy did

        Gtk layout:
        
        .------------- rb ---------------.
        | +name_vbox+ +----msg_vbox----+ |
        | |         | |                | |
        | | nick:   | | +------------+ | |
        | |         | | |    Text    | | |
        | +---------+ | +------------+ | |
        |             +----------------+ |
        `--------------------------------'

        """
        if buddy:
            nick = buddy.props.nick
            color = buddy.props.color
            try:
                color_stroke_html, color_fill_html = color.split(',')
            except ValueError:
                color_stroke_html, color_fill_html = ('#000000', '#888888')
            # Select text color based on fill color:
            color_fill_rgba = Color(color_fill_html).get_rgba()
            color_fill_gray = (color_fill_rgba[0] + color_fill_rgba[1] +
                               color_fill_rgba[2])/3
            color_stroke = Gdk.RGBA(*Color(color_stroke_html).get_rgba())
            color_fill = Gdk.RGBA(*color_fill_rgba)
            if color_fill_gray < 0.5:
                text_color = Gdk.RGBA(*COLOR_WHITE.get_rgba())
            else:
                text_color = Gdk.RGBA(*COLOR_BLACK.get_rgba())
        else:
            nick = '???'  # XXX: should be '' but leave for debugging

        logger.debug('Nick: %s' % nick)

        # Check for Right-To-Left languages:
        if Pango.find_base_dir(nick, -1) == Pango.Direction.RTL:
            lang_rtl = True
        else:
            lang_rtl = False

        logger.debug('lang_rtl: %s' % str(lang_rtl))

        # Check if new message box or add text to previous:
        new_msg = True
        if self._last_msg_sender:
            if not status_message:
                if buddy == self._last_msg_sender:
                    # Add text to previous message
                    new_msg = False

        if not new_msg:
            rb = self._last_msg
            msg_vbox = rb.get_children()[1]

        else: # Its a new_msg, we need to create a new rb
            # We are using an EventBox to get the border effect on rb
            eb = Gtk.EventBox()
            eb.override_background_color(Gtk.StateType.NORMAL, color_stroke)

            rb = Gtk.HBox()
            rb.override_background_color(Gtk.StateType.NORMAL, color_fill)
            rb.set_border_width(1)
            eb.add(rb)

            logger.debug('rb: %s' % str(rb))
            self._last_msg = rb
            self._last_msg_sender = buddy
            if not status_message:
                name = Gtk.TextView()
                text_buffer = name.get_buffer()
                text_buffer.set_text(nick + ':   ')
                name.override_font(FONT_BOLD.get_pango_desc())
                name.override_color(Gtk.StateType.NORMAL, text_color)
                name.override_background_color(Gtk.StateType.NORMAL, color_fill)
                name.set_border_width(5)
                name.show()
                name_vbox = Gtk.VBox()
                name_vbox.add(name)
                rb.pack_start(name_vbox, False, True, 0)

            msg_vbox = Gtk.VBox()
            rb.pack_start(msg_vbox, True, True, 0)

        if status_message:
            self._last_msg_sender = None

        if text:
            if not new_msg:
                msg = msg_vbox.get_children()[0]
                text_buffer = msg.get_buffer()
                text_buffer.set_text(text_buffer.get_text(text_buffer.get_start_iter(),
                                     text_buffer.get_end_iter(), True) + "\n" + text)
            else:
                msg = Gtk.TextView()
                text_buffer = msg.get_buffer()
                text_buffer.set_text(text)
                msg.show()
                msg.set_editable(False)
                msg.set_border_width(5)
                msg.set_justification(Gtk.Justification.LEFT)
                msg.override_font(FONT_NORMAL.get_pango_desc())
                msg.override_color(Gtk.StateType.NORMAL, text_color)
                msg.override_background_color(Gtk.StateType.NORMAL, color_fill)
                msg.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
                msg_vbox.pack_start(msg, True, True, 0)

        if new_msg:
            self.conversation.pack_start(eb, False, True, 0)
        eb.show_all()
        rb.show_all()

    def entry_activate_cb(self, entry):
        text = entry.get_text()
        entry.set_text('')
        logger.debug('Entry: %s' % text)
        if text:
            self.add_text(self.owner, text)
            if self.text_channel:
                self.text_channel.send(text)
            else:
                logger.debug('Tried to send message but text channel '
                    'not connected.')
Example #5
0
class MiniChat(Activity):
    def __init__(self, handle):
        Activity.__init__(self, handle)

        root = self.make_root()
        self.set_canvas(root)
        root.show_all()
        self.entry.grab_focus()

        toolbox = ActivityToolbox(self)
        activity_toolbar = toolbox.get_activity_toolbar()
        activity_toolbar.keep.props.visible = False
        self.set_toolbox(toolbox)
        toolbox.show()

        self.pservice = PresenceService()
        self.owner = self.pservice.get_owner()
        # Auto vs manual scrolling:
        self._scroll_auto = True
        self._scroll_value = 0.0
        # Track last message, to combine several messages:
        self._last_msg = None
        self._last_msg_sender = None
        self.text_channel = None

        if self._shared_activity:
            # we are joining the activity
            self.connect('joined', self._joined_cb)
            if self.get_shared():
                # we have already joined
                self._joined_cb()
        else:
            # we are creating the activity
            if not self.metadata or self.metadata.get('share-scope',
                    SCOPE_PRIVATE) == SCOPE_PRIVATE:
                # if we are in private session
                self._alert(_('Off-line'), _('Share, or invite someone.'))
            self.connect('shared', self._shared_cb)

    def _shared_cb(self, activity):
        logger.debug('Chat was shared')
        self._setup()

    def _joined_cb(self, activity):
        """Joined a shared activity."""
        if not self._shared_activity:
            return
        logger.debug('Joined a shared chat')
        for buddy in self._shared_activity.get_joined_buddies():
            self._buddy_already_exists(buddy)
        self._setup()

    def _setup(self):
        self.text_channel = TextChannelWrapper(
            self._shared_activity.telepathy_text_chan,
            self._shared_activity.telepathy_conn)
        self.text_channel.set_received_callback(self._received_cb)
        self._alert(_('On-line'), _('Connected'))
        self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
        self._shared_activity.connect('buddy-left', self._buddy_left_cb)
        self.entry.set_sensitive(True)
        self.entry.grab_focus()

    def _received_cb(self, buddy, text):
        """Show message that was received."""
        if buddy:
                nick = buddy.props.nick
        else:
            nick = '???'
        logger.debug('Received message from %s: %s', nick, text)
        self.add_text(buddy, text)

    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 _buddy_joined_cb (self, activity, buddy):
        """Show a buddy who joined"""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.props.nick
        else:
            nick = '???'
        self.add_text(buddy, buddy.props.nick+' '+_('joined the chat'),
            status_message=True)

    def _buddy_left_cb (self, activity, buddy):
        """Show a buddy who joined"""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.props.nick
        else:
            nick = '???'
        self.add_text(buddy, buddy.props.nick+' '+_('left the chat'),
            status_message=True)

    def _buddy_already_exists(self, buddy):
        """Show a buddy already in the chat."""
        if buddy == self.owner:
            return
        if buddy:
            nick = buddy.props.nick
        else:
            nick = '???'
        self.add_text(buddy, buddy.props.nick+' '+_('is here'),
            status_message=True)

    def make_root(self):
        conversation = hippo.CanvasBox(
            spacing=0,
            background_color=COLOR_WHITE.get_int())
        self.conversation = conversation

        entry = gtk.Entry()
        entry.modify_bg(gtk.STATE_INSENSITIVE,
                        COLOR_WHITE.get_gdk_color())
        entry.modify_base(gtk.STATE_INSENSITIVE,
                          COLOR_WHITE.get_gdk_color())
        entry.set_sensitive(False)
        entry.connect('activate', self.entry_activate_cb)
        self.entry = entry

        hbox = gtk.HBox()
        hbox.add(entry)

        sw = hippo.CanvasScrollbars()
        sw.set_policy(hippo.ORIENTATION_HORIZONTAL, hippo.SCROLLBAR_NEVER)
        sw.set_root(conversation)
        self.scrolled_window = sw
        
        vadj = self.scrolled_window.props.widget.get_vadjustment()
        vadj.connect('changed', self.rescroll)
        vadj.connect('value-changed', self.scroll_value_changed_cb)

        canvas = hippo.Canvas()
        canvas.set_root(sw)

        box = gtk.VBox(homogeneous=False)
        box.pack_start(hbox, expand=False)
        box.pack_start(canvas)

        return box

    def rescroll(self, adj, scroll=None):
        """Scroll the chat window to the bottom"""
        if self._scroll_auto:
            adj.set_value(adj.upper-adj.page_size)
            self._scroll_value = adj.get_value()

    def scroll_value_changed_cb(self, adj, scroll=None):
        """Turn auto scrolling on or off.
        
        If the user scrolled up, turn it off.
        If the user scrolled to the bottom, turn it back on.
        """
        if adj.get_value() < self._scroll_value:
            self._scroll_auto = False
        elif adj.get_value() == adj.upper-adj.page_size:
            self._scroll_auto = True

    def add_text(self, buddy, text, status_message=False):
        """Display text on screen, with name and colors.

        buddy -- buddy object
        text -- string, what the buddy said
        status_message -- boolean
            False: show what buddy said
            True: show what buddy did

        hippo layout:
        .------------- rb ---------------.
        | +name_vbox+ +----msg_vbox----+ |
        | |         | |                | |
        | | nick:   | | +--msg_hbox--+ | |
        | |         | | | text       | | |
        | +---------+ | +------------+ | |
        |             |                | |
        |             | +--msg_hbox--+ | |
        |             | | text       | | |
        |             | +------------+ | |
        |             +----------------+ |
        `--------------------------------'
        """
        if buddy:
            nick = buddy.props.nick
            color = buddy.props.color
            try:
                color_stroke_html, color_fill_html = color.split(',')
            except ValueError:
                color_stroke_html, color_fill_html = ('#000000', '#888888')
            # Select text color based on fill color:
            color_fill_rgba = Color(color_fill_html).get_rgba()
            color_fill_gray = (color_fill_rgba[0] + color_fill_rgba[1] +
                               color_fill_rgba[2])/3
            color_stroke = Color(color_stroke_html).get_int()
            color_fill = Color(color_fill_html).get_int()
            if color_fill_gray < 0.5:
                text_color = COLOR_WHITE.get_int()
            else:
                text_color = COLOR_BLACK.get_int()
        else:
            nick = '???'  # XXX: should be '' but leave for debugging
            color_stroke = COLOR_BLACK.get_int()
            color_fill = COLOR_WHITE.get_int()
            text_color = COLOR_BLACK.get_int()
            color = '#000000,#FFFFFF'

        # Check for Right-To-Left languages:
        if pango.find_base_dir(nick, -1) == pango.DIRECTION_RTL:
            lang_rtl = True
        else:
            lang_rtl = False

        # Check if new message box or add text to previous:
        new_msg = True
        if self._last_msg_sender:
            if not status_message:
                if buddy == self._last_msg_sender:
                    # Add text to previous message
                    new_msg = False

        if not new_msg:
            rb = self._last_msg
            msg_vbox = rb.get_children()[1]
            msg_hbox = hippo.CanvasBox(
                orientation=hippo.ORIENTATION_HORIZONTAL)
            msg_vbox.append(msg_hbox)
        else:
            rb = CanvasRoundBox(background_color=color_fill,
                                border_color=color_stroke,
                                padding=4)
            rb.props.border_color = color_stroke  # Bug #3742
            self._last_msg = rb
            self._last_msg_sender = buddy
            if not status_message:
                name = hippo.CanvasText(text=nick+':   ',
                    color=text_color,
                    font_desc=FONT_BOLD.get_pango_desc())
                name_vbox = hippo.CanvasBox(
                    orientation=hippo.ORIENTATION_VERTICAL)
                name_vbox.append(name)
                rb.append(name_vbox)
            msg_vbox = hippo.CanvasBox(
                orientation=hippo.ORIENTATION_VERTICAL)
            rb.append(msg_vbox)
            msg_hbox = hippo.CanvasBox(
                orientation=hippo.ORIENTATION_HORIZONTAL)
            msg_vbox.append(msg_hbox)

        if status_message:
            self._last_msg_sender = None

        if text:
            message = hippo.CanvasText(
                text=text,
                size_mode=hippo.CANVAS_SIZE_WRAP_WORD,
                color=text_color,
                font_desc=FONT_NORMAL.get_pango_desc(),
                xalign=hippo.ALIGNMENT_START)
            msg_hbox.append(message)

        # Order of boxes for RTL languages:
        if lang_rtl:
            msg_hbox.reverse()
            if new_msg:
                rb.reverse()

        if new_msg:
            box = hippo.CanvasBox(padding=2)
            box.append(rb)
            self.conversation.append(box)

    def entry_activate_cb(self, entry):
        text = entry.props.text
        logger.debug('Entry: %s' % text)
        if text:
            self.add_text(self.owner, text)
            entry.props.text = ''
            if self.text_channel:
                self.text_channel.send(text)
            else:
                logger.debug('Tried to send message but text channel '
                    'not connected.')