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.')
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) 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.')
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.')