Remmina - The GTK+ Remote Desktop Client  v1.4.34
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_chat_window.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009 - Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include <glib/gi18n.h>
38 #include <gdk/gdkkeysyms.h>
39 #include "remmina_chat_window.h"
41 
42 G_DEFINE_TYPE( RemminaChatWindow, remmina_chat_window, GTK_TYPE_WINDOW)
43 
44 enum {
45  SEND_SIGNAL,
47 };
48 
50 
52 {
53  TRACE_CALL(__func__);
54  remmina_chat_window_signals[SEND_SIGNAL] = g_signal_new("send", G_TYPE_FROM_CLASS(klass),
55  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaChatWindowClass, send), NULL, NULL,
56  g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
57 }
58 
60 {
61  TRACE_CALL(__func__);
62  window->history_text = NULL;
63  window->send_text = NULL;
64 }
65 
66 static void remmina_chat_window_clear_send_text(GtkWidget* widget, RemminaChatWindow* window)
67 {
68  TRACE_CALL(__func__);
69  GtkTextBuffer* buffer;
70 
71  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->send_text));
72  gtk_text_buffer_set_text(buffer, "", -1);
73  gtk_widget_grab_focus(window->send_text);
74 }
75 
77 {
78  TRACE_CALL(__func__);
79  GtkTextBuffer* buffer;
80  GtkTextIter iter;
81 
82  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->history_text));
83  gtk_text_buffer_get_end_iter(buffer, &iter);
84  gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(window->history_text), &iter, 0.0, FALSE, 0.0, 0.0);
85 
86  return FALSE;
87 }
88 
89 static void remmina_chat_window_append_text(RemminaChatWindow* window, const gchar* name, const gchar* tagname,
90  const gchar* text)
91 {
92  TRACE_CALL(__func__);
93  GtkTextBuffer* buffer;
94  GtkTextIter iter;
95  gchar* ptr;
96 
97  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->history_text));
98 
99  if (name) {
100  ptr = g_strdup_printf("(%s) ", name);
101  gtk_text_buffer_get_end_iter(buffer, &iter);
102  if (tagname) {
103  gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, ptr, -1, tagname, NULL);
104  }else {
105  gtk_text_buffer_insert(buffer, &iter, ptr, -1);
106  }
107  g_free(ptr);
108  }
109 
110  if (text && text[0] != 0) {
111  gtk_text_buffer_get_end_iter(buffer, &iter);
112  if (text[strlen(text) - 1] == '\n') {
113  gtk_text_buffer_insert(buffer, &iter, text, -1);
114  }else {
115  ptr = g_strdup_printf("%s\n", text);
116  gtk_text_buffer_insert(buffer, &iter, ptr, -1);
117  g_free(ptr);
118  }
119  }
120 
121  /* Use g_idle_add to make the scroll happen after the text has been actually updated */
122  g_idle_add((GSourceFunc)remmina_chat_window_scroll_proc, window);
123 }
124 
125 static void remmina_chat_window_send(GtkWidget* widget, RemminaChatWindow* window)
126 {
127  TRACE_CALL(__func__);
128  GtkTextBuffer* buffer;
129  GtkTextIter start, end;
130  gchar* text;
131 
132  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->send_text));
133  gtk_text_buffer_get_bounds(buffer, &start, &end);
134  text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
135 
136  if (!text || text[0] == '\0')
137  return;
138 
139  g_signal_emit_by_name(G_OBJECT(window), "send", text);
140 
141  remmina_chat_window_append_text(window, g_get_user_name(), "sender-foreground", text);
142 
143  g_free(text);
144 
145  remmina_chat_window_clear_send_text(widget, window);
146 }
147 
148 static gboolean remmina_chat_window_send_text_on_key(GtkWidget* widget, GdkEventKey* event, RemminaChatWindow* window)
149 {
150  TRACE_CALL(__func__);
151  if (event->keyval == GDK_KEY_Return) {
152  remmina_chat_window_send(widget, window);
153  return TRUE;
154  }
155  return FALSE;
156 }
157 
158 GtkWidget*
159 remmina_chat_window_new(GtkWindow* parent, const gchar* chat_with)
160 {
161  TRACE_CALL(__func__);
162  RemminaChatWindow* window;
163  gchar buf[100];
164  GtkWidget* grid;
165  GtkWidget* scrolledwindow;
166  GtkWidget* widget;
167  GtkWidget* image;
168  GtkTextBuffer* buffer;
169 
170  window = REMMINA_CHAT_WINDOW(g_object_new(REMMINA_TYPE_CHAT_WINDOW, NULL));
171 
172  if (parent) {
173  gtk_window_set_transient_for(GTK_WINDOW(window), parent);
174  }
175 
176  /* Title */
177  g_snprintf(buf, sizeof(buf), _("Chat with %s"), chat_with);
178  gtk_window_set_title(GTK_WINDOW(window), buf);
179  gtk_window_set_default_size(GTK_WINDOW(window), 450, 300);
180 
181  /* Main container */
182  grid = gtk_grid_new();
183  gtk_widget_show(grid);
184  gtk_grid_set_row_spacing(GTK_GRID(grid), 4);
185  gtk_grid_set_column_spacing(GTK_GRID(grid), 4);
186  gtk_container_set_border_width(GTK_CONTAINER(grid), 8);
187  gtk_container_add(GTK_CONTAINER(window), grid);
188 
189  /* Chat history */
190  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
191  gtk_widget_show(scrolledwindow);
192  gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolledwindow), 100);
193  gtk_widget_set_hexpand(GTK_WIDGET(scrolledwindow), TRUE);
194  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
195  gtk_grid_attach(GTK_GRID(grid), scrolledwindow, 0, 0, 3, 1);
196 
197  widget = gtk_text_view_new();
198  gtk_widget_show(widget);
199  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD_CHAR);
200  gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), FALSE);
201  gtk_container_add(GTK_CONTAINER(scrolledwindow), widget);
202 
203  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
204  gtk_text_buffer_create_tag(buffer, "sender-foreground", "foreground", "blue", NULL);
205  gtk_text_buffer_create_tag(buffer, "receiver-foreground", "foreground", "red", NULL);
206 
207  window->history_text = widget;
208 
209  /* Chat message to be sent */
210  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
211  gtk_widget_show(scrolledwindow);
212  gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolledwindow), 100);
213  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
214  gtk_widget_set_hexpand(GTK_WIDGET(scrolledwindow), TRUE);
215  gtk_grid_attach(GTK_GRID(grid), scrolledwindow, 0, 1, 3, 1);
216 
217  widget = gtk_text_view_new();
218  gtk_widget_show(widget);
219  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD_CHAR);
220  gtk_container_add(GTK_CONTAINER(scrolledwindow), widget);
221  g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(remmina_chat_window_send_text_on_key), window);
222 
223  window->send_text = widget;
224 
225  /* Send button */
226  image = gtk_image_new_from_icon_name("org.remmina.Remmina-document-send-symbolic", GTK_ICON_SIZE_BUTTON);
227  gtk_widget_show(image);
228 
229  widget = gtk_button_new_with_mnemonic(_("_Send"));
230  gtk_widget_show(widget);
231  gtk_button_set_image(GTK_BUTTON(widget), image);
232  gtk_grid_attach(GTK_GRID(grid), widget, 2, 2, 1, 1);
233  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_chat_window_send), window);
234 
235  /* Clear button */
236  image = gtk_image_new_from_icon_name("edit-clear", GTK_ICON_SIZE_BUTTON);
237  gtk_widget_show(image);
238 
239  widget = gtk_button_new_with_mnemonic(_("_Clear"));
240  gtk_widget_show(widget);
241  gtk_button_set_image(GTK_BUTTON(widget), image);
242  gtk_grid_attach(GTK_GRID(grid), widget, 1, 2, 1, 1);
243  g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(remmina_chat_window_clear_send_text), window);
244 
245  gtk_widget_grab_focus(window->send_text);
246 
247  return GTK_WIDGET(window);
248 }
249 
250 void remmina_chat_window_receive(RemminaChatWindow* window, const gchar* name, const gchar* text)
251 {
252  TRACE_CALL(__func__);
253  remmina_chat_window_append_text(window, name, "receiver-foreground", text);
254 }
255 
@ LAST_SIGNAL
Definition: rcw.c:183
static void remmina_chat_window_class_init(RemminaChatWindowClass *klass)
static gboolean remmina_chat_window_scroll_proc(RemminaChatWindow *window)
static void remmina_chat_window_clear_send_text(GtkWidget *widget, RemminaChatWindow *window)
void remmina_chat_window_receive(RemminaChatWindow *window, const gchar *name, const gchar *text)
static guint remmina_chat_window_signals[LAST_SIGNAL]
static gboolean remmina_chat_window_send_text_on_key(GtkWidget *widget, GdkEventKey *event, RemminaChatWindow *window)
GtkWidget * remmina_chat_window_new(GtkWindow *parent, const gchar *chat_with)
static void remmina_chat_window_send(GtkWidget *widget, RemminaChatWindow *window)
static void remmina_chat_window_init(RemminaChatWindow *window)
G_DEFINE_TYPE(RemminaChatWindow, remmina_chat_window, GTK_TYPE_WINDOW)
static void remmina_chat_window_append_text(RemminaChatWindow *window, const gchar *name, const gchar *tagname, const gchar *text)