def __init__(self, color, bg_color, lang_rtl): self._lang_rtl = lang_rtl gtk.TextView.__init__(self) self.set_editable(False) self.set_cursor_visible(False) self.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.get_buffer().set_text("", 0) self.iter_text = self.get_buffer().get_iter_at_offset(0) self.fg_tag = self.get_buffer().create_tag("foreground_color", foreground=color.get_html()) self._subscript_tag = self.get_buffer().create_tag('subscript', rise=-7 * pango.SCALE) # in pixels self._empty = True self.palette = None self._mouse_detector = MouseSpeedDetector(self, 200, 5) self._mouse_detector.connect('motion-slow', self.__mouse_slow_cb) self.modify_base(gtk.STATE_NORMAL, bg_color.get_gdk_color()) self.add_events(gtk.gdk.POINTER_MOTION_MASK | \ gtk.gdk.BUTTON_PRESS_MASK | \ gtk.gdk.BUTTON_RELEASE_MASK | \ gtk.gdk.LEAVE_NOTIFY_MASK) self.connect('event-after', self.__event_after_cb) self.connect('button-press-event', self.__button_press_cb) self.motion_notify_id = self.connect('motion-notify-event', \ self.__motion_notify_cb) self.connect('visibility-notify-event', self.__visibility_notify_cb) self.connect('leave-notify-event', self.__leave_notify_event_cb)
class TextBox(gtk.TextView): hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2) def __init__(self, color, bg_color, lang_rtl): self._lang_rtl = lang_rtl gtk.TextView.__init__(self) self.set_editable(False) self.set_cursor_visible(False) self.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.get_buffer().set_text("", 0) self.iter_text = self.get_buffer().get_iter_at_offset(0) self.fg_tag = self.get_buffer().create_tag("foreground_color", foreground=color.get_html()) self._subscript_tag = self.get_buffer().create_tag('subscript', rise=-7 * pango.SCALE) # in pixels self._empty = True self.palette = None self._mouse_detector = MouseSpeedDetector(self, 200, 5) self._mouse_detector.connect('motion-slow', self.__mouse_slow_cb) self.modify_base(gtk.STATE_NORMAL, bg_color.get_gdk_color()) self.add_events(gtk.gdk.POINTER_MOTION_MASK | \ gtk.gdk.BUTTON_PRESS_MASK | \ gtk.gdk.BUTTON_RELEASE_MASK | \ gtk.gdk.LEAVE_NOTIFY_MASK) self.connect('event-after', self.__event_after_cb) self.connect('button-press-event', self.__button_press_cb) self.motion_notify_id = self.connect('motion-notify-event', \ self.__motion_notify_cb) self.connect('visibility-notify-event', self.__visibility_notify_cb) self.connect('leave-notify-event', self.__leave_notify_event_cb) def __leave_notify_event_cb(self, widget, event): self._mouse_detector.stop() def __button_press_cb(self, widget, event): if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: # To disable the standard textview popup return True # Links can be activated by clicking. def __event_after_cb(self, widget, event): if event.type != gtk.gdk.BUTTON_RELEASE: return False x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y)) iter_tags = self.get_iter_at_location(x, y) for tag in iter_tags.get_tags(): url = tag.get_data('url') if url is not None: if event.button == 3: palette = tag.get_data('palette') xw, yw = self.get_toplevel().get_pointer() palette.move(int(xw), int(yw)) palette.popup() else: self._show_via_journal(url) break return False def _show_via_journal(self, url): """Ask the journal to display a URL""" logging.debug('Create journal entry for URL: %s', url) jobject = datastore.create() metadata = { 'title': "%s: %s" % (_('URL from Chat'), url), 'title_set_by_user': '******', 'icon-color': profile.get_color().to_string(), 'mime_type': 'text/uri-list', } for k, v in metadata.items(): jobject.metadata[k] = v file_path = join(get_activity_root(), 'instance', '%i_' % time.time()) open(file_path, 'w').write(url + '\r\n') os.chmod(file_path, 0755) jobject.set_file_path(file_path) datastore.write(jobject) show_object_in_journal(jobject.object_id) jobject.destroy() os.unlink(file_path) def check_url_hovering(self, x, y): # Looks at all tags covering the position (x, y) in the text view, # and if one of them is a link return True hovering = False # When check on_slow_mouse event, the position can be out # of the widget and return negative values. if x < 0 or y < 0: return hovering self.palette = None iter_tags = self.get_iter_at_location(x, y) tags = iter_tags.get_tags() for tag in tags: url = tag.get_data('url') self.palette = tag.get_data('palette') if url is not None: hovering = True break return hovering def set_cursor_if_appropriate(self, x, y): # Looks at all tags covering the position (x, y) in the text view, # and if one of them is a link, change the cursor to the "hands" cursor hovering_over_link = self.check_url_hovering(x, y) win = self.get_window(gtk.TEXT_WINDOW_TEXT) if hovering_over_link: win.set_cursor(self.hand_cursor) self._mouse_detector.start() else: win.set_cursor(None) self._mouse_detector.stop() def __mouse_slow_cb(self, widget): x, y = self.get_pointer() hovering_over_link = self.check_url_hovering(x, y) if hovering_over_link: if self.palette is not None: xw, yw = self.get_toplevel().get_pointer() self.palette.move(xw, yw) self.palette.popup() self._mouse_detector.stop() else: if self.palette is not None: self.palette.popdown() # Update the cursor image if the pointer moved. def __motion_notify_cb(self, widget, event): x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y)) self.set_cursor_if_appropriate(x, y) self.window.get_pointer() return False def __visibility_notify_cb(self, widget, event): # Also update the cursor image if the window becomes visible # (e.g. when a window covering it got iconified). wx, wy, __ = self.window.get_pointer() bx, by = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, wx, wy) self.set_cursor_if_appropriate(bx, by) return False def __palette_mouse_enter_cb(self, widget, event): self.handler_block(self.motion_notify_id) def __palette_mouse_leave_cb(self, widget, event): self.handler_unblock(self.motion_notify_id) def add_text(self, text): buf = self.get_buffer() if not self._empty: buf.insert(self.iter_text, '\n') words = text.split() for word in words: if _URL_REGEXP.search(word) is not None: tag = buf.create_tag(None, foreground="blue", underline=pango.UNDERLINE_SINGLE) tag.set_data("url", word) palette = _URLMenu(word) palette.connect('enter-notify-event', self.__palette_mouse_enter_cb) palette.connect('leave-notify-event', self.__palette_mouse_leave_cb) tag.set_data('palette', palette) buf.insert_with_tags(self.iter_text, word, tag, self.fg_tag) else: for i in smilies.parse(word): if isinstance(i, gtk.gdk.Pixbuf): start = self.iter_text.get_offset() buf.insert_pixbuf(self.iter_text, i) buf.apply_tag(self._subscript_tag, buf.get_iter_at_offset(start), self.iter_text) else: buf.insert_with_tags(self.iter_text, i, self.fg_tag) buf.insert_with_tags(self.iter_text, ' ', self.fg_tag) self._empty = False
class TextBox(gtk.TextView): hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2) def __init__(self, color, bg_color, lang_rtl): self._lang_rtl = lang_rtl gtk.TextView.__init__(self) self.set_editable(False) self.set_cursor_visible(False) self.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.get_buffer().set_text("", 0) self.iter_text = self.get_buffer().get_iter_at_offset(0) self.fg_tag = self.get_buffer().create_tag("foreground_color", foreground=color.get_html()) self._subscript_tag = self.get_buffer().create_tag('subscript', rise=-7 * pango.SCALE) # in pixels self._empty = True self.palette = None self._mouse_detector = MouseSpeedDetector(self, 200, 5) self._mouse_detector.connect('motion-slow', self.__mouse_slow_cb) self.modify_base(gtk.STATE_NORMAL, bg_color.get_gdk_color()) self.add_events(gtk.gdk.POINTER_MOTION_MASK | \ gtk.gdk.BUTTON_PRESS_MASK | \ gtk.gdk.BUTTON_RELEASE_MASK | \ gtk.gdk.LEAVE_NOTIFY_MASK) self.connect('event-after', self.__event_after_cb) self.connect('button-press-event', self.__button_press_cb) self.motion_notify_id = self.connect('motion-notify-event', \ self.__motion_notify_cb) self.connect('visibility-notify-event', self.__visibility_notify_cb) self.connect('leave-notify-event', self.__leave_notify_event_cb) def __leave_notify_event_cb(self, widget, event): self._mouse_detector.stop() def __button_press_cb(self, widget, event): if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: # To disable the standard textview popup return True # Links can be activated by clicking. def __event_after_cb(self, widget, event): if event.type != gtk.gdk.BUTTON_RELEASE: return False x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y)) iter_tags = self.get_iter_at_location(x, y) for tag in iter_tags.get_tags(): url = tag.get_data('url') if url is not None: if event.button == 3: palette = tag.get_data('palette') xw, yw = self.get_toplevel().get_pointer() palette.move(int(xw), int(yw)) palette.popup() else: self._show_via_journal(url) break return False def _show_via_journal(self, url): """Ask the journal to display a URL""" logging.debug('Create journal entry for URL: %s', url) jobject = datastore.create() metadata = { 'title': "%s: %s" % (_('URL from Chat'), url), 'title_set_by_user': '******', 'icon-color': profile.get_color().to_string(), 'mime_type': 'text/uri-list', } for k, v in metadata.items(): jobject.metadata[k] = v file_path = join(get_activity_root(), 'instance', '%i_' % time.time()) open(file_path, 'w').write(url + '\r\n') os.chmod(file_path, 0755) jobject.set_file_path(file_path) datastore.write(jobject) show_object_in_journal(jobject.object_id) jobject.destroy() os.unlink(file_path) def check_url_hovering(self, x, y): # Looks at all tags covering the position (x, y) in the text view, # and if one of them is a link return True hovering = False # When check on_slow_mouse event, the position can be out # of the widget and return negative values. if x < 0 or y < 0: return hovering self.palette = None iter_tags = self.get_iter_at_location(x, y) tags = iter_tags.get_tags() for tag in tags: url = tag.get_data('url') self.palette = tag.get_data('palette') if url is not None: hovering = True break return hovering def set_cursor_if_appropriate(self, x, y): # Looks at all tags covering the position (x, y) in the text view, # and if one of them is a link, change the cursor to the "hands" cursor hovering_over_link = self.check_url_hovering(x, y) win = self.get_window(gtk.TEXT_WINDOW_TEXT) if hovering_over_link: win.set_cursor(self.hand_cursor) self._mouse_detector.start() else: win.set_cursor(None) self._mouse_detector.stop() def __mouse_slow_cb(self, widget): x, y = self.get_pointer() hovering_over_link = self.check_url_hovering(x, y) if hovering_over_link: if self.palette is not None: xw, yw = self.get_toplevel().get_pointer() self.palette.move(xw, yw) self.palette.popup() self._mouse_detector.stop() else: if self.palette is not None: self.palette.popdown() # Update the cursor image if the pointer moved. def __motion_notify_cb(self, widget, event): x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y)) self.set_cursor_if_appropriate(x, y) self.window.get_pointer() return False def __visibility_notify_cb(self, widget, event): # Also update the cursor image if the window becomes visible # (e.g. when a window covering it got iconified). wx, wy, __ = self.window.get_pointer() bx, by = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, wx, wy) self.set_cursor_if_appropriate(bx, by) return False def __palette_mouse_enter_cb(self, widget, event): self.handler_block(self.motion_notify_id) def __palette_mouse_leave_cb(self, widget, event): self.handler_unblock(self.motion_notify_id) def add_text(self, text): buf = self.get_buffer() if not self._empty: buf.insert(self.iter_text, '\n') words = text.split() for word in words: if _URL_REGEXP.match(word) is not None: tag = buf.create_tag(None, foreground="blue", underline=pango.UNDERLINE_SINGLE) tag.set_data("url", word) palette = _URLMenu(word) palette.connect('enter-notify-event', self.__palette_mouse_enter_cb) palette.connect('leave-notify-event', self.__palette_mouse_leave_cb) tag.set_data('palette', palette) buf.insert_with_tags(self.iter_text, word, tag, self.fg_tag) else: for i in smilies.parse(word): if isinstance(i, gtk.gdk.Pixbuf): start = self.iter_text.get_offset() buf.insert_pixbuf(self.iter_text, i) buf.apply_tag(self._subscript_tag, buf.get_iter_at_offset(start), self.iter_text) else: buf.insert_with_tags(self.iter_text, i, self.fg_tag) buf.insert_with_tags(self.iter_text, ' ', self.fg_tag) self._empty = False