#import "JubGtkChatUI.h"
+#import "JubGtkHelper.h"
-struct show_chat_params {
- GtkWidget *window;
- OFString *title;
+struct call_send_block_params {
+ jub_send_block_t block;
+ JubGtkChatUI *chat;
};
-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;
+ struct call_send_block_params *params = data;
+ if (event->keyval != GDK_KEY_Return &&
+ event->keyval != GDK_KEY_KP_Enter) 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);
+
+ if ([text length] > 0) {
+ gtk_entry_set_text(entry, "");
+ params->block(text);
+ [params->chat addMessage: text
+ sender: @"You"];
+ }
+
[text release];
return TRUE;
}
-static gboolean add_tags(gpointer data) {
- gtk_text_buffer_create_tag(data, "bold", "weight",
- PANGO_WEIGHT_BOLD, NULL);
-
- return FALSE;
+static void call_close_block(GtkWidget *object, gpointer data)
+{
+ jub_close_block_t block = data;
+ block();
}
@implementation JubGtkChatUI
- initWithTitle: (OFString*)title
- sendBlock: (jub_send_block_t)sendBlock_
+ closeBlock: (jub_close_block_t)closeBlock
+ 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);
+ __block volatile BOOL initialized = NO;
+ __block GtkWidget *chat_window;
+ __block GtkTextBuffer *chat_buffer;
+
+ _closeBlock = [closeBlock copy];
+ _sendBlock = [sendBlock copy];
+ _bufferEmpty = YES;
+
+ g_idle_add_block(^{
+ GtkEntry *chat_entry;
+
+ GtkBuilder *builder = gtk_builder_new();
+ gtk_builder_add_from_file(builder, "data/gtk/chat.ui",
+ NULL);
+
+ chat_window = GTK_WIDGET(
+ gtk_builder_get_object(builder, "ChatWindow"));
+ g_signal_connect(chat_window, "destroy",
+ G_CALLBACK(call_close_block), closeBlock);
+
+ _chat_view = GTK_TEXT_VIEW(
+ gtk_builder_get_object(builder, "ChatTextView"));
+ chat_buffer = gtk_text_view_get_buffer(_chat_view);
+ gtk_text_buffer_create_tag(chat_buffer, "bold",
+ "weight", PANGO_WEIGHT_BOLD, NULL);
+
+ chat_entry = GTK_ENTRY(
+ gtk_builder_get_object(builder, "ChatEntry"));
+ struct call_send_block_params *send_params =
+ [self allocMemoryWithSize: sizeof(*send_params)];
+ send_params->block = sendBlock;
+ send_params->chat = self;
+ g_signal_connect(chat_entry, "key_release_event",
+ G_CALLBACK(call_send_block), send_params);
+
+ gtk_window_set_title(GTK_WINDOW(chat_window),
+ [title UTF8String]);
+ gtk_widget_show(chat_window);
+
+ initialized = YES;
+
+ g_object_unref(builder);
+ });
+
+ while (!initialized);
+
+ _chat_buffer = chat_buffer;
+ _chat_window = chat_window;
} @catch (id e) {
[self release];
@throw e;
- (void)dealloc
{
- gtk_widget_destroy(chat_window);
- [sendBlock release];
+ gtk_widget_destroy(_chat_window);
+ [_sendBlock release];
+ [_closeBlock release];
[super dealloc];
}
- (void)addMessage: (OFString*)text
- sender: (OFString*)sender;
+ 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;
+ BOOL first = _bufferEmpty;
+ if (OF_UNLIKELY(_bufferEmpty)) _bufferEmpty = NO;
+
+ g_idle_add_block(^{
+ GtkTextIter end_iter;
+ GtkTextMark *insert_mark =
+ gtk_text_buffer_get_insert(_chat_buffer);
+
+ if (OF_LIKELY(!first))
+ gtk_text_buffer_insert_at_cursor(_chat_buffer, "\n", 1);
+
+ gtk_text_buffer_get_end_iter(_chat_buffer, &end_iter);
+ gtk_text_buffer_insert_with_tags_by_name(_chat_buffer,
+ &end_iter, [sender UTF8String], [sender UTF8StringLength],
+ "bold", NULL);
+ gtk_text_buffer_get_end_iter(_chat_buffer, &end_iter);
+ gtk_text_buffer_insert_with_tags_by_name(_chat_buffer,
+ &end_iter, ": ", 2, "bold", NULL);
+
+ gtk_text_buffer_insert_at_cursor(_chat_buffer,
+ [text UTF8String], [text UTF8StringLength]);
+
+ gtk_text_view_scroll_to_mark(_chat_view, insert_mark, 0,
+ FALSE, 0, 0);
+ });
}
@end