--- /dev/null
+#import "JubGtkChatUI.h"
+
+struct show_chat_params {
+ GtkWidget *window;
+ OFString *title;
+};
+
+static gboolean show_chat(gpointer data)
+{
+ struct show_chat_params *params = data;
+
+ gtk_window_set_title(GTK_WINDOW(params->window),
+ [params->title UTF8String]);
+ gtk_widget_show(params->window);
+
+ g_object_unref(params->window);
+ [params->title release];
+ free(params);
+
+ return FALSE;
+}
+
+struct add_text_params {
+ OFString *name;
+ OFString *text;
+ GtkTextBuffer *buffer;
+ BOOL first;
+};
+
+static gboolean add_text(gpointer data)
+{
+ GtkTextIter endIter;
+ struct add_text_params *params = data;
+
+ if (OF_LIKELY(!params->first))
+ gtk_text_buffer_insert_at_cursor(params->buffer, "\n", 1);
+
+ gtk_text_buffer_get_end_iter(params->buffer, &endIter);
+ gtk_text_buffer_insert_with_tags_by_name(params->buffer, &endIter,
+ [params->name UTF8String], [params->name UTF8StringLength], "bold",
+ NULL);
+ gtk_text_buffer_get_end_iter(params->buffer, &endIter);
+ gtk_text_buffer_insert_with_tags_by_name(params->buffer, &endIter,
+ ": ", 2, "bold", NULL);
+
+ gtk_text_buffer_insert_at_cursor(params->buffer,
+ [params->text UTF8String], [params->text UTF8StringLength]);
+
+ [params->name release];
+ [params->text release];
+ g_object_unref(params->buffer);
+ free(params);
+
+ return FALSE;
+}
+
+static gboolean call_send_block(GtkEntry *entry, GdkEventKey *event,
+ gpointer data)
+{
+ if (event->keyval != GDK_KEY_Return) return TRUE;
+
+ jub_send_block_t block = data;
+ OFString *text =
+ [[OFString alloc] initWithUTF8String: gtk_entry_get_text(entry)];
+ gtk_entry_set_text(entry, "");
+ block(text);
+ [text release];
+
+ return TRUE;
+}
+
+static gboolean add_tags(gpointer data) {
+ gtk_text_buffer_create_tag(data, "bold", "weight",
+ PANGO_WEIGHT_BOLD, NULL);
+
+ return FALSE;
+}
+
+@implementation JubGtkChatUI
+- initWithTitle: (OFString*)title
+ sendBlock: (jub_send_block_t)sendBlock_
+{
+ self = [super init];
+
+ @try {
+ GtkTextView *chat_view;
+ GtkEntry *chat_entry;
+ GtkBuilder *builder = gtk_builder_new();
+
+ sendBlock = [sendBlock_ copy];
+ bufferEmpty = YES;
+
+ gtk_builder_add_from_file(builder, "data/gtk/chat.ui", NULL);
+
+ chat_window = GTK_WIDGET(
+ gtk_builder_get_object(builder, "ChatWindow"));
+
+ chat_view = GTK_TEXT_VIEW(
+ gtk_builder_get_object(builder, "ChatTextView"));
+
+ chat_entry = GTK_ENTRY(
+ gtk_builder_get_object(builder, "ChatEntry"));
+
+ g_signal_connect(chat_entry, "key_release_event",
+ G_CALLBACK(call_send_block), sendBlock);
+
+ chat_buffer = gtk_text_view_get_buffer(chat_view);
+ g_idle_add(add_tags, chat_buffer);
+
+ struct show_chat_params *params = malloc(sizeof(*params));
+ params->window = g_object_ref(chat_window);
+ params->title = [title retain];
+ g_idle_add(show_chat, params);
+
+ g_object_unref(builder);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ gtk_widget_destroy(chat_window);
+ [sendBlock release];
+
+ [super dealloc];
+}
+
+- (void)addMessage: (OFString*)text
+ sender: (OFString*)sender;
+{
+ struct add_text_params *params = malloc(sizeof(*params));
+ params->name = [sender retain];
+ params->text = [text retain];
+ params->buffer = g_object_ref(chat_buffer);
+ params->first = bufferEmpty;
+ g_idle_add(add_text, params);
+ if (OF_UNLIKELY(bufferEmpty)) bufferEmpty = NO;
+}
+@end