What is the difficulty here? Of course the simple fact that a message can contain a message and that any such recursive display must also display all pieces of the message that it itself is viewing. It’s indeed recursive. By now, all people who can’t grasp recursive loops should have decided to stop reading this. If not, you will suffer.
Let me start at the begin. A message is a mime part and a mime part can, but not must, contain one or more other mime parts. I defined a TnyMsgView, which is an interface that inherits from the TnyMimePartView interface. I created a bunch of implementations.
A TnyGtkMsgView which simply implements TnyMsgView and TnyMimePartView, a TnyGtkMsgWindow which decorates the TnyGtkMsgView (and also implements these interfaces, as that is the decorator design pattern), a TnyMozEmbedMsgView which inherits the TnyGtkMsgView.
I also implemented a bunch of mime part viewers that are not TnyMsgView implementations, just TnyMimePartView implementations: A TnyGtkTextMimePartView, a TnyMozEmbedHtmlMimePartView, A TnyGtkAttachmentMimePartView and soon we will also have a TnyICalMimePartView and others.
What will happen? Say you have a message with a text/plain body and a message/rfc822 mime part with a text/html mime part. This means that somebody clicked on a spam E-mail, pressed the forward button of his MUA, filled in your E-mail address and you received that E-mail. Not unusual, right?
I know you don’t (you don’t have to tell me Kris) but let us assume you want to see the HTML E-mail formatted using HTML. Yet you don’t want to double click on the attached E-mail, you want to view it inline in the message preview pane. All popular E-mail clients support this, right? The tinymail framework supports it out of the box too.
What will happen is that the developer using tinymail will have selected the constructor of TnyMozEmbedMsgView in the platform factory for creating a TnyMsgView instance. Maybe he created that implementation himself? Maybe he inherited from TnyGtkMsgView? Both are supported and possible, the factory returns the interface. We will assume he decided to use a standard component of tinymail.
When setting the message (the model) of this view, it will loop all mime parts. For each mime part in the message, the TnyMsgView implementation will have a method that creates a suitable TnyMimePartView instance. The HTML one overrides this method and creates one that can display HTML in case the mime part is a text/html one, else it calls the method on super. In case it’s an message/rfc822 mime part, the TnyMsgView implementation will simply create a new instance of its own type. It must also implement TnyMimePartView, remember? Therefore it can be used as a mime part view too. It becomes recursive without losing code readability.
To take it even further, the TnyGtkMsgWindow simply decorates any TnyMsgView implementation that eventually inherits from GtkWidget. It decorates this by wrapping it in a GtkWindow. Its implementation is extremely simple, yet that was all that was necessary to support opening messages in a window too. Transparent for the developer, because it too implements the same interfaces. Obviously shouldn’t this decorator really create a new instance of itself when a message/rfc822 mime part must be viewed. You can’t embed a GtkWindow in a GtkWindow (maybe you can, but we don’t have to do it this way). You simply keep decorating it. Also the method that creates a new instance of the self or this instance. That way it’ll return the decorated type, not the decorator type. Which is a GtkWindow.
From my web statistics I know a lot people are reading tinymail’s code using trac. For those people, if you want to learn the real purpose of all these funny design patterns and for-some-people not straight forward techniques, this is the code that you should read:
- TnyGtkMsgView is the base and default message viewer. It has to do classification in such a way that it doesn’t have to know about the for himself alien viewers. You will usually inherit this type. For each mime part it will create a TnyMimePartView using its create-mime-part-view-for method (which is overridden by types that inherit TnyGtkWindow). For message/rfc822 mime parts, the method will create a new instance of the same type as self using the create_new_inline_viewer method (which is also overridden by types that inherit TnyGtkWindow).
- TnyGtkWindow is a decorator for TnyGtkMsgView that wraps the widget in a GtkWindow (the screenshot demos this).
- TnyMozEmbedView overrides create-mime-part-view-for where it returns a TnyMozEmbedHtmlMimePartView. It’s of course just a sample HTML component. Multiple HTML components can be and will be supported by default in future. It also overrides the create_new_new_inline_viewer method so that it can return a HTML capable viewer for inlined messages too (the screenshot demos this).