import gtk import gtk.gdk import pango import gobject import gnome import gnomevfs import gettext _ = gettext.gettext from gnomeblog import html_converter from gnomeblog import hig_alert class RichEntry(gtk.TextView): def __init__(self): self.buffer = gtk.TextBuffer() gtk.TextView.__init__(self, self.buffer) self._initDND() self.set_editable(True) self.set_wrap_mode(gtk.WRAP_WORD) self.set_pixels_below_lines(16) html_converter.para_tag = self.buffer.create_tag("p") html_converter.para_tag.opening_tag = "
" html_converter.para_tag.closing_tag = "
" self.connect("event-after", self._onEventAfter) self.buffer.connect_after("insert_text", self._onInsertText) self.toggle_list = [] # Hack for addHyperlink with old pygtk's self.linknum = 0 self.images = [ ] def _initDND(self): # GtkTextView already defines drag and drop targets, get them old_targets = self.drag_dest_get_target_list() # Unfortunately old_targets string field has a funny type # that needs to be explicitly converted to string targets = [] for target in old_targets: targets.append((str(target[0]), target[1], target[2])) # Add the DND type that we support and set the new list of types targets.insert(0, ("text/uri-list", 0, 398)) self.drag_dest_set_target_list(targets) self.connect("drag-data-received", self._onDragDataReceived) def _getUniqueTag(self): if (gtk.pygtk_version[0] < 2 and gtk.pygtk_version[1] < 100 and gtk.pygtk_version[2] < 16): # this is an older version of pygtk that doesn't # have a way to create anonymous tags, hack around it link_tag = self.buffer.create_tag("unique%d" % (self.linknum)) self.linknum = self.linknum + 1 print "Old Pygtk, using hack to work around lack of anonymous tags" else: link_tag = self.buffer.create_tag() return link_tag def addHyperlink(self, iter, text, uri, on_activate): link_tag = self._getUniqueTag() link_tag.set_property("underline", pango.UNDERLINE_SINGLE) link_tag.set_property("foreground", "#0000FF") link_tag.opening_tag = '' % (uri) link_tag.closing_tag = '' link_tag.uri = uri link_tag.on_activate = on_activate link_tag.hyperlink = True self.buffer.insert_with_tags(iter, text, link_tag) def applyHyperlink(self, selection, uri, on_activate): link_tag = self._getUniqueTag() link_tag.set_property("underline", pango.UNDERLINE_SINGLE) link_tag.set_property("foreground", "#0000FF") link_tag.opening_tag = '' % (uri) link_tag.closing_tag = '' link_tag.uri = uri link_tag.on_activate = on_activate link_tag.hyperlink = True self.buffer.apply_tag(link_tag, selection[0], selection[1]) def getImages(self): return self.images def getHTML(self): return html_converter.getHTML(self.buffer) def clear(self): self.buffer.delete(self.buffer.get_start_iter(), self.buffer.get_end_iter()) def createStyleToggle(self, pango_markup_properties, stock_button, html_tag): tag = self.buffer.create_tag(html_tag) for property in pango_markup_properties: tag.set_property(property[0], property[1]) toggle = StyleToggle(stock_button, tag, html_tag, self) self.toggle_list.append(toggle) return toggle def _onDragDataReceived(self, widget, drag_context, window_x, window_y, selection_data, info, timestamp): if (info == 398): # 398 means text/uri-list assert(selection_data.format == 8) uri_list = selection_data.data uris = uri_list.split('\r\n') (buffer_x, buffer_y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, window_x, window_y) dnd_iter = self.get_iter_at_location(buffer_x, buffer_y) for uri in uris: if (len(uri) <= 0): continue loader = gtk.gdk.PixbufLoader() try: file_info = gnomevfs.get_file_info(uri, gnomevfs.FILE_INFO_GET_MIME_TYPE) file_contents = gnomevfs.read_entire_file(uri) except gnomevfs.Error, e: hig_alert.reportError(_("Could not load dragged in image"), _("Error loading %s was: %s") % (hig_alert.italic(uri), e)) loader.write(file_contents) try: loader.close() pixbuf = loader.get_pixbuf() except gobject.GError, e: pixbuf = None hig_alert.reportError(_("Could not load dragged in image"), _("Error loading %s was: %s") % (hig_alert.italic(uri), e)) if (pixbuf): # Create a new image tag image_tag = self._getUniqueTag() image_tag.opening_tag = "" image_tag.closing_tag = "" image_tag.file_contents = file_contents paths = uri.split('/') image_tag.name = paths[len(paths) - 1] image_tag.mime_type = file_info.mime_type self.images.append(image_tag) # Insert the pixbuf, and then set the new image # tag around it so that we can generate its HTML later start_offset = dnd_iter.get_offset() self.buffer.insert_pixbuf(dnd_iter, pixbuf) start = self.buffer.get_iter_at_offset(start_offset) self.buffer.apply_tag(image_tag, start, dnd_iter) else: print ("Some other sort of data received") def _onInsertText(self, widget, pos, text, length): for style in self.toggle_list: if (style.get_active() == True): pos_end = pos.copy() pos_end.backward_chars(length) self.buffer.apply_tag(style.style_tag, pos, pos_end) def _onEventAfter(self, widget, event): if event.type != gtk.gdk.BUTTON_RELEASE or event.button != 1: return False bounds = self.buffer.get_selection_bounds() if not bounds: return False x, y = self.window_to_buffer_coords (gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y)) iter = self.get_iter_at_location(x, y) for tag in iter.get_tags(): try: if tag.hyperlink == True: # Found Hyperlink, activating tag.on_activate(tag.uri) except AttributeError: pass return False class InsertHyperlinkButton(gtk.Button): def __init__(self, rich_entry): gtk.Button.__init__(self, _("Add _Link...")) self.rich_entry = rich_entry self.connect("clicked", self._onClicked) def _onClicked(self, button): dialog = gtk.Dialog(_("Add Link"), parent=self.get_toplevel(), buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, _("_Add Link"), gtk.RESPONSE_ACCEPT)) dialog.set_default_response(gtk.RESPONSE_ACCEPT) dialog.set_has_separator(False) dialog.set_resizable(False) dialog.set_border_width(5) dialog.vbox.set_spacing(2) textLabel = gtk.Label(_("Text:")) textLabel.set_alignment(0.0, 0.5) urlLabel = gtk.Label(_("URL:")) urlLabel.set_alignment(0.0, 0.5) textEntry = gtk.Entry() urlEntry = gtk.Entry() urlEntry.set_activates_default(True) selection = self.rich_entry.buffer.get_selection_bounds() if selection: textEntry.set_text(self.rich_entry.buffer.get_text(selection[0],selection[1])) table = gtk.Table(rows=2, columns=2) table.set_border_width(5) table.set_row_spacings(6) table.set_col_spacings(12) table.attach(textLabel, 0, 1, 0, 1, xoptions=gtk.FILL) table.attach(urlLabel, 0, 1, 1, 2, xoptions=gtk.FILL) table.attach(textEntry, 1, 2, 0, 1) table.attach(urlEntry, 1, 2, 1, 2) dialog.vbox.pack_start(table) table.show_all() if selection: urlEntry.grab_focus() else: textEntry.grab_focus() response = dialog.run() if response == gtk.RESPONSE_ACCEPT: if selection: self.rich_entry.applyHyperlink(selection, urlEntry.get_text(), self._onHyperlinkClicked) else: iter = self.rich_entry.buffer.get_iter_at_mark(self.rich_entry.buffer.get_mark("insert")) self.rich_entry.addHyperlink(iter, textEntry.get_text(), urlEntry.get_text(), self._onHyperlinkClicked) dialog.hide() self.rich_entry.grab_focus() def _onHyperlinkClicked(self, uri): print "Clicked %s" % (uri) class StyleToggle(gtk.ToggleButton): def __init__(self, stock_icon_id, tag, htmltag, text_view): gtk.ToggleButton.__init__(self) tag.opening_tag = '<%s>' % (htmltag) tag.closing_tag = '%s>' % (htmltag) self.style_tag = tag self.text_view = text_view self.text_buffer = text_view.get_buffer() self.cursor_mark = self.text_buffer.get_mark("insert") self.connect("clicked", self._onStyleToggleActivate) image = gtk.Image() image.set_from_stock(stock_icon_id, gtk.ICON_SIZE_SMALL_TOOLBAR) self.add(image) image.show() self.text_buffer.connect("mark-set", self._onMarkSet) def _onStyleToggleActivate(self, toggle): if self.programmatic_toggle: return selection = self.text_buffer.get_selection_bounds() if selection: # There's a selection, apply/remove style tag to it if self.get_active(): self.text_buffer.apply_tag(self.style_tag, selection[0], selection[1]) else: self.text_buffer.remove_tag(self.style_tag, selection[0], selection[1]) self.text_view.grab_focus() def _onMarkSet(self, textbuffer, iter, mark): if mark == self.cursor_mark: self.programmatic_toggle = True if iter.has_tag(self.style_tag): self.set_active(True) else: self.set_active(False) self.programmatic_toggle = False