GNOME Bugzilla – Bug 324177
Copy and paste of rich text
Last modified: 2006-03-07 13:50:37 UTC
Attached patch from maemo-gtk adds rich text copy and paste to GtkTextBuffer. I made the API private, even though it's public in maemo. Also it might make sense to add versioning to the serialized data.
Created attachment 56027 [details] [review] Patch against HEAD implementing the above
Created attachment 56028 [details] gtktextbufferserialize.c
Created attachment 56029 [details] gtktextbufferserialize.h
- This should probably be renamed to can-transfer-rich-text, and cover drag-and-drop as well. - While this ad-hoc xml format is probably fine for transferring data between text views in multiple instances of the same application, I can see that it could be much more useful to offer "rich text" in other formats, such as RTF or XHTML fragments. One way to enable this would be to allow registration of additional formats (to register a format, you would need a name or atom, and a pair of serialization/deserialization functions) - What about selectable labels ? It would probably be of marginal usefulness, but those can also contain "rich text" and support copy-paste/drag-and-drop.
- I don't see how can-transfer-rich-text would be better, since copying is always allowed, the property just makes pasting configurable. - Totally agree here, however getting RTF or XHTML done right is much harder. And does it hurt to support this as "internal" feature between different GtkTextViews? - How can entries contain rich text?
- The proposed name change was just to make it more obvious that it applies to copy-and-paste and dnd (Different ways to "transfer" text...) - I don't think there is a problem with allowing this internal format between different text views (you should probably document it then, though). And I don't want to hold this patch hostage to implementing RTF support in GTK+, but having hooks for other formats would be useful. - Not entries, selectable labels
i'd like to suggest that we simply change the header from its current "RICHTEXT" to something like "GTKTEXTBUFFERSERIAL0000", i.e. do the versioning mitch suggested earlier. then we can declare the format purely internal, don't need to document it and can change it to any (documented) format in the future without problems.
Yes, something like GTKTEXTBUFFERCONTENTS-1.0 is a better name for the private serialization format. I still think my points in #4 need to be addressed.
Created attachment 57645 [details] [review] Next version of the patch Attached patch addresses serialization format versioning and DND. Registering of other rich text formats is not implemented, but easier to implement now because the target lists/tables are manages dynamically now. GtkLabel stuff is also untouched.
Created attachment 57646 [details] Next version of gtktextbufferserialize.c
Created attachment 57647 [details] Next version of gtktextbufferserialize.h
The one thing thats hampering our ability to add registration of application-defined rich text formats later is the assumption that a text buffer can only support a single rich text format. You will have to allow multiple formats. In order to extend this to selectable labels, we need a mapping from pango attributes to tags, and a functions to serialize/deserialize text+pango attributes into this format.
I don't consider that patch final, just a step in the right direction. The target lists/tables are already created dynamically, so adding registration of other formats is much easier to implement. In fact, I'm working on an API to allow this. The problem I see with selectable labels is that it's not exactly trivial to create a serialization API that will handle both. Perhaps we should simply have a utility function that makes a GtkTextBuffer out of a label's markup? Also, where should the serialization formats be registered? With each buffer and label? That sounds rather weird imho. Maybe it's best to put all that stuff into a central rich text format registry, living in gtk/gtkrichtext.[ch], and have both GtkTextBuffer and GtkLabel use this?
matthis, note that mitch's patch already supports multiple formats that are negotiated upon pasting (either you use a specific format name which then denotes a conventional set of tags, or you use a NULL format which allowes any kind of tag list to be transfered). to avoid duplication of the logic involved, and because i consider rich-text pasting from labels far less common than plain text pasting from labels or rich-text pasting from text buffers, i think mitch's idea to -if neccessary- facilitate label->text-buffer->serialization conversion is very sound. programatically, label markup conversion to and from text buffers can also be interesting, so having two conversion functions that accomplish this sounds desirable to me.
I don't think that's the "formats" matthias means, we're talking about optionally letting the user register functions for RTF, XHTML etc. The set of aggreed-upon (in whatever context) tags that are supported is a layer on top of that.
Yes. I agree that rich text copying from labels can be treated as a separate problem after we have text views done. And Mitch is correct that I was thinking about scenarios where the application, might want to allow copying contents as RTF or XHTML. I think it is quite reasonable to assume that people will want to allow multiple different formats to be available at the same time. Just like we offer plain text in multiple formats, and let the receiving side pick the one it can handle best. I agree with mitch that it would be cumbersome to register the RTF format for every single text view where you want to offer it. It might in fact be better to have an extensible set of rich text targets, similar to the current gtk_target_list_add_text/image_targets() The text view would then simply offer rich text in all formats which have been registered with GtkSelection (or whereever we want to establish that registry)
Created attachment 58030 [details] API proposal: gtkrichtext.h registry Attached API proposal for central rich text format registry. gtkselection.c would simply use the registry to implement: void gtk_target_list_add_image_targets (GtkTargetList *list, guint info, gboolean deserializable, const gchar *tag_set_name);
Ehm, s/image/rich_text/ for the target list API of course...
Looks reasonable to me. Do you expect it to be common to only support one way, ie only serialize, or only deserialize ? If not, it may be more convenient to register both directions at the same time.
Registering both was my first API version, but after some thinking it turned out that not having separate internal serialize/deserialize format lists would make certain things impossible, like overriding just one of the functions some other part of the code has registered before. So I went for the more complex API, instead of figuring the lack thereof after the release ;)
hmm, when would you do that ? anyway, its not a big deal, I'm fine with having separate functions
I have no idea :) But given the fact that each combination of mime-type and tag-set-name (e.g. "application/my-private-foo-format;my-editor-tag-set") makes up a new format, it's hard to forsee what weird stuff people will do with that, so better stay generic...
Created attachment 58162 [details] [review] Next patch version for review Attached patch makes use the rich text registration facility that follows in the next attachments.
Created attachment 58163 [details] gtkrichtext.c
Created attachment 58164 [details] gtkrichtext.h
Created attachment 58165 [details] gtktextbufferserialize.c
Created attachment 58166 [details] gtktextbufferserialize.h That's it.
Created attachment 58207 [details] [review] Rename property: s/rich-text-format/rich-text-tag-set-name/ Next version featuring GtkTextBuffer property name cleanup. Matthias: Does not include the list of tag-set-names yet we talked about on irc.
Created attachment 58473 [details] [review] Next patch version - rename "tag set" to "tagset" globally to avoind looking like a setter - changed tagset name APIs in GtkTextBuffer and GtkClipboard from one string to a GList of strings so a buffer can support multiple tagsets - allways offer all formats when copying/dragging, and only restrict pasting/dropping - changed text buffer property and API: can-transfer-rich-text -> can-receive-rich-text
ok, first feedback after playing around with it for 5 minutes: + I was able to copy and paste formatted text including a pixbuf from one testtext instance to another, very nice + application/x-gtk-text-buffer-rich-text shows up in the list of targets offered when pasting and when dragging. - DND between the two testtext instances only transfers poor text, even though application/x-gtk-text-buffer-rich-text is among the offered formats - The DND icon needs to show rich text when we are dragging rich text
*** Bug 328660 has been marked as a duplicate of this bug. ***
Created attachment 58655 [details] [review] Fix dropping between different buffers in the same process
g_free (priv->rich_text_tagsets); in gtk_text_buffer_finalize should be g_list_free (priv->rich_text_tagsets);
Mitch, "fix dropping between different buffers in the same process" means don't drop rich text when can-reveive-rich-text is off ? can-receive-rich-text is still ignored for copy-paste or dnd inside a single buffer, afaics. But it works between different buffers.
Created attachment 58674 [details] [review] patch
things to fix: - hide cursor - make text fade out towards the right/bottom - don't serialize/deserialize for this, probably add a separate function to create a rich text drag icon from a buffer slice - reinstate the simple text drag icon function for entries.
Created attachment 58816 [details] [review] Adds rich text DND icon
In some cases (e.g. syntax highlighting) copying/dragging text with formatting is not desired. At the moment when copying/pasting in one buffer, it also copies tags, and workaround for this is using some flag in apply_tags handler (ugly but works). Please make sure it doesn't break (Mitch said he didn't change intra-buffer copying, so it should be okay). Also, dragging colored text from a buffer with syntax highlighting inside might not make much sense, so it would be good to have some property to disable it (source targets can't be just changed without code for dnd, right?). And same thing applies to pixbufs - e.g. placeholders which denote certain positions in text but are not part of the text.
> In some cases (e.g. syntax highlighting) copying/dragging > text with formatting is not desired. On the contrary, I think that it is desirable to copy some code from your text editor to your email or im client, and send it to your buddy with full syntax highlighting. When I send people code snippets, I sometimes even manually syntax highlight them. Syntax highlighting lets people see the structure of code easier. When the code gets put in another text editor or gets fed to an interpreter, that's when the syntax highlighting information needs to get stripped. Coincidentially, interpreters and code editors don't accept rich text. Part of my motivation for filing a duplicate of this feature request was that I wanted to be able to copy and paste syntax-highlighted code.
Created attachment 60309 [details] gtkrichtext.c
Created attachment 60310 [details] gtkrichtext.h
Created attachment 60311 [details] [review] new patch to be used with above new gtkrichtext.[ch] New rich text patch: - the serialize/deserialize registries are now per-textbuffer. I didn't change the namespace yet, comments are welcome (probably gtk_text_buffer_[de]desialize_foo_bar()) - tagset names are gone from the public API, except for the rich text register functions. They are meant to be used with either the internal format (mime_type and functions == NULL), or with any externally provided format that is capable of holding any of a text buffer's features. - creating new tags upon deserialization is now configurable for each deserialize function, it didn't make any sense to configure this on the text buffer. - the rich text functions in gtkclipboard and gtkselection now take GtkTextBuffer arguments, since they need to query the rich text formats registered with the buffer. - the internal format is not registered by default any more. - a text case is still missing, testing by simply setting some text buffer properties is not possible any longer.
Created attachment 60399 [details] gtktextbufferrichtext.c
Created attachment 60400 [details] gtktextbufferrichtext.h
Created attachment 60401 [details] [review] And another iteration We're getting close: - renamed rich text registry files to gtktextbufferrichtext.[ch] and its functions to gtk_text_buffer_foo_bar() - added testing stuff to tests/testtext.c - added "content_buffer" parameter to the main serialize() and deserialize() functions, because now that the registry is attached to the buffer, it's no longer possible to create temporary buffers and serialize/deserialize them, they simply lack the registered rich text formats. With the new API, a text buffer can (de)serialize another one. The patch uses this feature to serialize the temporary clipboard contents buffer that is created internally. - the only thing that's missing is some API docs.
The api changes look good to me. When I tried testtext, I could not get rich text dragged from one buffer to the other, although I had registered application/x-gtk-text-buffer-rich-text on both sides, and checked the can create tags column. Copying inside a single buffer, on the other hand copied the tags even though no rich text target was registered.
Ah, the failure to copy rich text between buffers was due to the can-receive-rich-text property not being set.
Copying inside a text buffer does not go through rich text. And I wonder if we still need the can-receive-rich-text property now that rich text has to be registered for each buffer.
I was about to ask the same, but you said the version was final...
Created attachment 60595 [details] gtktextbufferrichtext.c
Created attachment 60596 [details] gtktextbufferrichtext.h
Created attachment 60597 [details] gtktextbufferserialize.c
Created attachment 60598 [details] gtktextbufferserialize.h
Created attachment 60599 [details] [review] Next patch version Apparently, nothing is ever final :) Changes in this version: - the can-receive-rich-text property and API is gone - the actual serialize and deserialize functions have two GtkTextBuffer parameters too, so they have access to the buffer they were registered with - there is now a separate, very easy API for registering tagset names for the internal rich text format. This also makes the main registering functions easier to understand and document - there are some API docs to ease review, more to come
Looks good to me. GtkTextBufferSelectionInfo does not really need to become public API, does it ?
ok, i think the API is ok now, and this can go into CVS. i do think that text buffers in general should support copying of the unconstrained rich-text format, by adding: gtk_text_buffer_register_serialize_tagset (buffer, NULL); to gtk_text_buffer_init(). since tags need to be handled by the user, pastage couldn't be enabled by default. but this way, at least every text buffer can serve as a source for target buffers that are setup with: atom = gtk_text_buffer_register_deserialize_tagset (buffer, NULL); gtk_text_buffer_deserialize_set_can_create_tags (buffer, atom, TRUE);
GtkTextBufferSelectionInfo contains the "info" values used in the target lists returned by gtk_text_buffer_get_copy,paste_target_list(), i don't see a way to avoid them being public. Will add gtk_text_buffer_register_serialize_tagset (buffer, NULL); to gtk_text_buffer_init() plus the missing api docs and commit the patch otherwise as-is.
argh, mis-clicked
Fixed in CVS with the final changes discussed above: 2006-03-07 Michael Natterer <mitch@imendio.com> Add infrastructure for copy/paste and DND of rich text for GtkTextBuffer. Fixes bug #324177. * gtk/gtktextbufferrichtext.[ch]: new files implementing a per-buffer registry of rich text formats. * gtk/gtk.h: #include gtktextbufferrichtext.h * gtk/gtktextbufferserialize.[ch]: new files implementing an internal serialization format that can handle all of a text buffer's tags and pixbufs. It's not useful for anything except tranfer between instances of GtkTextBuffer (Anders Carlsson). * gtk/Makefile.am: build the new files. * gtk/gtkclipboard.[ch]: added convenience APIs for rich text, just as they exist for plain text and pixbufs. * gtk/gtkselection.[ch]: added rich text convenience APIs here too. Return the target list from gtk_target_list_ref(). Register GtkTargetList as boxed type. Added gtk_target_table_new_from_list() and gtk_target_table_free(), which make converting between GtkTargetList and arrays of GtkTargetEntry considerably easier. * gtk/gtktextutil.[ch]: added _gtk_text_util_create_rich_drag_icon() which creates a fancy rich text icon (Matthias Clasen). * gtk/gtktextbuffer.[ch]: use all the new stuff above and implement copy and paste of rich text. Added APIs for getting the target lists used for copy and paste. Added public enum GtkTextBufferTargetInfo which contains the "info" IDs associated with the entries of the target lists. * gtk/gtktextview.c: use the new rich text APIs and GtkTextBuffer's new target list API to enable DND of rich text chunks. * gtk/gtk.symbols: export all the new symbols added. * tests/testtext.c: added rich text testing stuff.
Created attachment 60832 [details] [review] For the records: final patch as comitted to cvs