diff options
Diffstat (limited to '')
-rw-r--r-- | src/remmina_chat_window.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/src/remmina_chat_window.c b/src/remmina_chat_window.c new file mode 100644 index 0000000..da58fb4 --- /dev/null +++ b/src/remmina_chat_window.c @@ -0,0 +1,255 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2009 - Vic Lee + * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo + * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include "remmina_chat_window.h" +#include "remmina/remmina_trace_calls.h" + +G_DEFINE_TYPE( RemminaChatWindow, remmina_chat_window, GTK_TYPE_WINDOW) + +enum { + SEND_SIGNAL, + LAST_SIGNAL +}; + +static guint remmina_chat_window_signals[LAST_SIGNAL] = { 0 }; + +static void remmina_chat_window_class_init(RemminaChatWindowClass* klass) +{ + TRACE_CALL(__func__); + remmina_chat_window_signals[SEND_SIGNAL] = g_signal_new("send", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaChatWindowClass, send), NULL, NULL, + g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static void remmina_chat_window_init(RemminaChatWindow* window) +{ + TRACE_CALL(__func__); + window->history_text = NULL; + window->send_text = NULL; +} + +static void remmina_chat_window_clear_send_text(GtkWidget* widget, RemminaChatWindow* window) +{ + TRACE_CALL(__func__); + GtkTextBuffer* buffer; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->send_text)); + gtk_text_buffer_set_text(buffer, "", -1); + gtk_widget_grab_focus(window->send_text); +} + +static gboolean remmina_chat_window_scroll_proc(RemminaChatWindow* window) +{ + TRACE_CALL(__func__); + GtkTextBuffer* buffer; + GtkTextIter iter; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->history_text)); + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(window->history_text), &iter, 0.0, FALSE, 0.0, 0.0); + + return FALSE; +} + +static void remmina_chat_window_append_text(RemminaChatWindow* window, const gchar* name, const gchar* tagname, + const gchar* text) +{ + TRACE_CALL(__func__); + GtkTextBuffer* buffer; + GtkTextIter iter; + gchar* ptr; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->history_text)); + + if (name) { + ptr = g_strdup_printf("(%s) ", name); + gtk_text_buffer_get_end_iter(buffer, &iter); + if (tagname) { + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, ptr, -1, tagname, NULL); + }else { + gtk_text_buffer_insert(buffer, &iter, ptr, -1); + } + g_free(ptr); + } + + if (text && text[0] != 0) { + gtk_text_buffer_get_end_iter(buffer, &iter); + if (text[strlen(text) - 1] == '\n') { + gtk_text_buffer_insert(buffer, &iter, text, -1); + }else { + ptr = g_strdup_printf("%s\n", text); + gtk_text_buffer_insert(buffer, &iter, ptr, -1); + g_free(ptr); + } + } + + /* Use g_idle_add to make the scroll happen after the text has been actually updated */ + g_idle_add((GSourceFunc)remmina_chat_window_scroll_proc, window); +} + +static void remmina_chat_window_send(GtkWidget* widget, RemminaChatWindow* window) +{ + TRACE_CALL(__func__); + GtkTextBuffer* buffer; + GtkTextIter start, end; + gchar* text; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->send_text)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + + if (!text || text[0] == '\0') + return; + + g_signal_emit_by_name(G_OBJECT(window), "send", text); + + remmina_chat_window_append_text(window, g_get_user_name(), "sender-foreground", text); + + g_free(text); + + remmina_chat_window_clear_send_text(widget, window); +} + +static gboolean remmina_chat_window_send_text_on_key(GtkWidget* widget, GdkEventKey* event, RemminaChatWindow* window) +{ + TRACE_CALL(__func__); + if (event->keyval == GDK_KEY_Return) { + remmina_chat_window_send(widget, window); + return TRUE; + } + return FALSE; +} + +GtkWidget* +remmina_chat_window_new(GtkWindow* parent, const gchar* chat_with) +{ + TRACE_CALL(__func__); + RemminaChatWindow* window; + gchar buf[100]; + GtkWidget* grid; + GtkWidget* scrolledwindow; + GtkWidget* widget; + GtkWidget* image; + GtkTextBuffer* buffer; + + window = REMMINA_CHAT_WINDOW(g_object_new(REMMINA_TYPE_CHAT_WINDOW, NULL)); + + if (parent) { + gtk_window_set_transient_for(GTK_WINDOW(window), parent); + } + + /* Title */ + g_snprintf(buf, sizeof(buf), _("Chat with %s"), chat_with); + gtk_window_set_title(GTK_WINDOW(window), buf); + gtk_window_set_default_size(GTK_WINDOW(window), 450, 300); + + /* Main container */ + grid = gtk_grid_new(); + gtk_widget_show(grid); + gtk_grid_set_row_spacing(GTK_GRID(grid), 4); + gtk_grid_set_column_spacing(GTK_GRID(grid), 4); + gtk_container_set_border_width(GTK_CONTAINER(grid), 8); + gtk_container_add(GTK_CONTAINER(window), grid); + + /* Chat history */ + scrolledwindow = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwindow); + gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolledwindow), 100); + gtk_widget_set_hexpand(GTK_WIDGET(scrolledwindow), TRUE); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_grid_attach(GTK_GRID(grid), scrolledwindow, 0, 0, 3, 1); + + widget = gtk_text_view_new(); + gtk_widget_show(widget); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD_CHAR); + gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), FALSE); + gtk_container_add(GTK_CONTAINER(scrolledwindow), widget); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); + gtk_text_buffer_create_tag(buffer, "sender-foreground", "foreground", "blue", NULL); + gtk_text_buffer_create_tag(buffer, "receiver-foreground", "foreground", "red", NULL); + + window->history_text = widget; + + /* Chat message to be sent */ + scrolledwindow = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwindow); + gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolledwindow), 100); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_widget_set_hexpand(GTK_WIDGET(scrolledwindow), TRUE); + gtk_grid_attach(GTK_GRID(grid), scrolledwindow, 0, 1, 3, 1); + + widget = gtk_text_view_new(); + gtk_widget_show(widget); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD_CHAR); + gtk_container_add(GTK_CONTAINER(scrolledwindow), widget); + g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(remmina_chat_window_send_text_on_key), window); + + window->send_text = widget; + + /* Send button */ + image = gtk_image_new_from_icon_name("org.remmina.Remmina-document-send-symbolic", GTK_ICON_SIZE_BUTTON); + gtk_widget_show(image); + + widget = gtk_button_new_with_mnemonic(_("_Send")); + gtk_widget_show(widget); + gtk_button_set_image(GTK_BUTTON(widget), image); + gtk_grid_attach(GTK_GRID(grid), widget, 2, 2, 1, 1); + g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_chat_window_send), window); + + /* Clear button */ + image = gtk_image_new_from_icon_name("edit-clear", GTK_ICON_SIZE_BUTTON); + gtk_widget_show(image); + + widget = gtk_button_new_with_mnemonic(_("_Clear")); + gtk_widget_show(widget); + gtk_button_set_image(GTK_BUTTON(widget), image); + gtk_grid_attach(GTK_GRID(grid), widget, 1, 2, 1, 1); + g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_chat_window_clear_send_text), window); + + gtk_widget_grab_focus(window->send_text); + + return GTK_WIDGET(window); +} + +void remmina_chat_window_receive(RemminaChatWindow* window, const gchar* name, const gchar* text) +{ + TRACE_CALL(__func__); + remmina_chat_window_append_text(window, name, "receiver-foreground", text); +} + |