diff options
Diffstat (limited to 'src/ui/new_lobby.c')
-rw-r--r-- | src/ui/new_lobby.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/src/ui/new_lobby.c b/src/ui/new_lobby.c new file mode 100644 index 0000000..0a9fe37 --- /dev/null +++ b/src/ui/new_lobby.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2022 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /* | ||
21 | * @author Tobias Frisch | ||
22 | * @file ui/new_lobby.c | ||
23 | */ | ||
24 | |||
25 | #include "new_lobby.h" | ||
26 | |||
27 | #include "../application.h" | ||
28 | |||
29 | static void | ||
30 | handle_warning_info_bar_close(GtkInfoBar *info_bar, | ||
31 | UNUSED gpointer user_data) | ||
32 | { | ||
33 | gtk_info_bar_set_revealed(info_bar, FALSE); | ||
34 | } | ||
35 | |||
36 | static void | ||
37 | handle_warning_info_bar_response(GtkInfoBar *info_bar, | ||
38 | UNUSED int response_id, | ||
39 | UNUSED gpointer user_data) | ||
40 | { | ||
41 | gtk_info_bar_set_revealed(info_bar, FALSE); | ||
42 | } | ||
43 | |||
44 | static void | ||
45 | handle_cancel_button_click(UNUSED GtkButton *button, | ||
46 | gpointer user_data) | ||
47 | { | ||
48 | GtkDialog *dialog = GTK_DIALOG(user_data); | ||
49 | gtk_window_close(GTK_WINDOW(dialog)); | ||
50 | } | ||
51 | |||
52 | void | ||
53 | handle_lobby_opened_and_uri_generated(void *cls, | ||
54 | const struct GNUNET_CHAT_Uri *uri) | ||
55 | { | ||
56 | MESSENGER_Application *app = (MESSENGER_Application*) cls; | ||
57 | |||
58 | if (app->ui.new_lobby.qr) | ||
59 | QRcode_free(app->ui.new_lobby.qr); | ||
60 | |||
61 | if (!uri) | ||
62 | { | ||
63 | if (app->ui.new_lobby.preview_stack) | ||
64 | gtk_stack_set_visible_child( | ||
65 | app->ui.new_lobby.preview_stack, | ||
66 | app->ui.new_lobby.fail_box | ||
67 | ); | ||
68 | |||
69 | app->ui.new_lobby.qr = NULL; | ||
70 | return; | ||
71 | } | ||
72 | |||
73 | gchar *uri_string = GNUNET_CHAT_uri_to_string(uri); | ||
74 | |||
75 | app->ui.new_lobby.qr = QRcode_encodeString( | ||
76 | uri_string, | ||
77 | 0, | ||
78 | QR_ECLEVEL_L, | ||
79 | QR_MODE_8, | ||
80 | 0 | ||
81 | ); | ||
82 | |||
83 | if (app->ui.new_lobby.id_drawing_area) | ||
84 | gtk_widget_queue_draw(GTK_WIDGET(app->ui.new_lobby.id_drawing_area)); | ||
85 | |||
86 | if (app->ui.new_lobby.preview_stack) | ||
87 | gtk_stack_set_visible_child( | ||
88 | app->ui.new_lobby.preview_stack, | ||
89 | GTK_WIDGET(app->ui.new_lobby.id_drawing_area) | ||
90 | ); | ||
91 | |||
92 | if (app->ui.new_lobby.id_entry) | ||
93 | gtk_entry_set_text(app->ui.new_lobby.id_entry, uri_string); | ||
94 | |||
95 | GNUNET_free(uri_string); | ||
96 | |||
97 | if (!(app->ui.new_lobby.id_entry)) | ||
98 | return; | ||
99 | |||
100 | const gint id_length = gtk_entry_get_text_length(app->ui.new_lobby.id_entry); | ||
101 | |||
102 | gtk_widget_set_sensitive( | ||
103 | GTK_WIDGET(app->ui.new_lobby.copy_button), | ||
104 | id_length > 0? TRUE : FALSE | ||
105 | ); | ||
106 | } | ||
107 | |||
108 | static void | ||
109 | handle_generate_button_click(UNUSED GtkButton *button, | ||
110 | gpointer user_data) | ||
111 | { | ||
112 | MESSENGER_Application *app = (MESSENGER_Application*) user_data; | ||
113 | |||
114 | GtkTreeModel *model = gtk_combo_box_get_model( | ||
115 | app->ui.new_lobby.expiration_combo_box | ||
116 | ); | ||
117 | |||
118 | gulong delay = 0; | ||
119 | |||
120 | GtkTreeIter iter; | ||
121 | if (gtk_combo_box_get_active_iter(app->ui.new_lobby.expiration_combo_box, | ||
122 | &iter)) | ||
123 | gtk_tree_model_get(model, &iter, 1, &delay, -1); | ||
124 | |||
125 | struct GNUNET_TIME_Relative expiration = GNUNET_TIME_relative_multiply( | ||
126 | GNUNET_TIME_relative_get_second_(), | ||
127 | delay | ||
128 | ); | ||
129 | |||
130 | gtk_stack_set_visible_child( | ||
131 | app->ui.new_lobby.preview_stack, | ||
132 | GTK_WIDGET(app->ui.new_lobby.loading_spinner) | ||
133 | ); | ||
134 | |||
135 | gtk_stack_set_visible_child( | ||
136 | app->ui.new_lobby.stack, | ||
137 | app->ui.new_lobby.copy_box | ||
138 | ); | ||
139 | |||
140 | gtk_widget_set_sensitive(GTK_WIDGET(app->ui.new_lobby.copy_button), FALSE); | ||
141 | |||
142 | gtk_widget_set_visible(GTK_WIDGET(app->ui.new_lobby.generate_button), FALSE); | ||
143 | gtk_widget_set_visible(GTK_WIDGET(app->ui.new_lobby.copy_button), TRUE); | ||
144 | |||
145 | GNUNET_CHAT_lobby_open( | ||
146 | app->chat.messenger.handle, | ||
147 | expiration, | ||
148 | handle_lobby_opened_and_uri_generated, | ||
149 | app | ||
150 | ); | ||
151 | } | ||
152 | |||
153 | static void | ||
154 | handle_copy_button_click(UNUSED GtkButton *button, | ||
155 | gpointer user_data) | ||
156 | { | ||
157 | MESSENGER_Application *app = (MESSENGER_Application*) user_data; | ||
158 | |||
159 | const gint id_length = gtk_entry_get_text_length(app->ui.new_lobby.id_entry); | ||
160 | const gchar *id_text = gtk_entry_get_text(app->ui.new_lobby.id_entry); | ||
161 | |||
162 | GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); | ||
163 | |||
164 | if ((clipboard) && (id_length > 0)) | ||
165 | gtk_clipboard_set_text(clipboard, id_text, id_length); | ||
166 | |||
167 | gtk_window_close(GTK_WINDOW(app->ui.new_lobby.dialog)); | ||
168 | } | ||
169 | |||
170 | static void | ||
171 | handle_dialog_destroy(UNUSED GtkWidget *window, | ||
172 | gpointer user_data) | ||
173 | { | ||
174 | ui_new_lobby_dialog_cleanup((UI_NEW_LOBBY_Handle*) user_data); | ||
175 | } | ||
176 | |||
177 | static gboolean | ||
178 | handle_id_drawing_area_draw(GtkWidget* drawing_area, | ||
179 | cairo_t* cairo, | ||
180 | gpointer user_data) | ||
181 | { | ||
182 | UI_NEW_LOBBY_Handle *handle = (UI_NEW_LOBBY_Handle*) user_data; | ||
183 | |||
184 | GtkStyleContext* context = gtk_widget_get_style_context(drawing_area); | ||
185 | |||
186 | if (!context) | ||
187 | return FALSE; | ||
188 | |||
189 | const guint width = gtk_widget_get_allocated_width(drawing_area); | ||
190 | const guint height = gtk_widget_get_allocated_height(drawing_area); | ||
191 | |||
192 | gtk_render_background(context, cairo, 0, 0, width, height); | ||
193 | |||
194 | if ((!(handle->qr)) || (handle->qr->width <= 0)) | ||
195 | return FALSE; | ||
196 | |||
197 | const guint m = 3; | ||
198 | const guint w = handle->qr->width; | ||
199 | const guint w2 = w + m * 2; | ||
200 | |||
201 | guchar pixels [w2 * w2 * 3]; | ||
202 | |||
203 | guint x, y, z; | ||
204 | for (y = 0; y < w2; y++) | ||
205 | for (x = 0; x < w2; x++) | ||
206 | { | ||
207 | guchar value; | ||
208 | |||
209 | if ((x >= m) && (y >= m) && (x - m < w) && (y - m < w)) | ||
210 | value = ((handle->qr->data[(y - m) * w + x - m]) & 1); | ||
211 | else | ||
212 | value = 0; | ||
213 | |||
214 | for (z = 0; z < 3; z++) | ||
215 | pixels[(y * w2 + x) * 3 + z] = value? 0x00 : 0xff; | ||
216 | } | ||
217 | |||
218 | GdkPixbuf *image = gdk_pixbuf_new_from_data( | ||
219 | pixels, | ||
220 | GDK_COLORSPACE_RGB, | ||
221 | FALSE, | ||
222 | 8, | ||
223 | w2, | ||
224 | w2, | ||
225 | w2 * 3, | ||
226 | NULL, | ||
227 | NULL | ||
228 | ); | ||
229 | |||
230 | if (!image) | ||
231 | return FALSE; | ||
232 | |||
233 | int dwidth = gdk_pixbuf_get_width(image); | ||
234 | int dheight = gdk_pixbuf_get_height(image); | ||
235 | |||
236 | double ratio_width = 1.0 * width / dwidth; | ||
237 | double ratio_height = 1.0 * height / dheight; | ||
238 | |||
239 | const double ratio = ratio_width < ratio_height? ratio_width : ratio_height; | ||
240 | |||
241 | dwidth = (int) (dwidth * ratio); | ||
242 | dheight = (int) (dheight * ratio); | ||
243 | |||
244 | double dx = (width - dwidth) * 0.5; | ||
245 | double dy = (height - dheight) * 0.5; | ||
246 | |||
247 | const int interp_type = (ratio >= 1.0? | ||
248 | GDK_INTERP_NEAREST : | ||
249 | GDK_INTERP_BILINEAR | ||
250 | ); | ||
251 | |||
252 | GdkPixbuf* scaled = gdk_pixbuf_scale_simple( | ||
253 | image, | ||
254 | dwidth, | ||
255 | dheight, | ||
256 | interp_type | ||
257 | ); | ||
258 | |||
259 | gtk_render_icon(context, cairo, scaled, dx, dy); | ||
260 | |||
261 | cairo_fill(cairo); | ||
262 | |||
263 | g_object_unref(scaled); | ||
264 | g_object_unref(image); | ||
265 | |||
266 | return FALSE; | ||
267 | } | ||
268 | |||
269 | |||
270 | void | ||
271 | ui_new_lobby_dialog_init(MESSENGER_Application *app, | ||
272 | UI_NEW_LOBBY_Handle *handle) | ||
273 | { | ||
274 | handle->builder = gtk_builder_new_from_resource( | ||
275 | application_get_resource_path(app, "ui/new_lobby.ui") | ||
276 | ); | ||
277 | |||
278 | handle->dialog = GTK_DIALOG( | ||
279 | gtk_builder_get_object(handle->builder, "new_lobby_dialog") | ||
280 | ); | ||
281 | |||
282 | gtk_window_set_transient_for( | ||
283 | GTK_WINDOW(handle->dialog), | ||
284 | GTK_WINDOW(app->ui.messenger.main_window) | ||
285 | ); | ||
286 | |||
287 | handle->warning_info_bar = GTK_INFO_BAR( | ||
288 | gtk_builder_get_object(handle->builder, "warning_info_bar") | ||
289 | ); | ||
290 | |||
291 | g_signal_connect( | ||
292 | handle->warning_info_bar, | ||
293 | "close", | ||
294 | G_CALLBACK(handle_warning_info_bar_close), | ||
295 | NULL | ||
296 | ); | ||
297 | |||
298 | g_signal_connect( | ||
299 | handle->warning_info_bar, | ||
300 | "response", | ||
301 | G_CALLBACK(handle_warning_info_bar_response), | ||
302 | NULL | ||
303 | ); | ||
304 | |||
305 | handle->stack = GTK_STACK( | ||
306 | gtk_builder_get_object(handle->builder, "new_lobby_stack") | ||
307 | ); | ||
308 | |||
309 | handle->generate_box = GTK_WIDGET( | ||
310 | gtk_builder_get_object(handle->builder, "generate_box") | ||
311 | ); | ||
312 | |||
313 | handle->copy_box = GTK_WIDGET( | ||
314 | gtk_builder_get_object(handle->builder, "copy_box") | ||
315 | ); | ||
316 | |||
317 | handle->expiration_combo_box = GTK_COMBO_BOX( | ||
318 | gtk_builder_get_object(handle->builder, "expiration_combo_box") | ||
319 | ); | ||
320 | |||
321 | handle->preview_stack = GTK_STACK( | ||
322 | gtk_builder_get_object(handle->builder, "preview_stack") | ||
323 | ); | ||
324 | |||
325 | handle->fail_box = GTK_WIDGET( | ||
326 | gtk_builder_get_object(handle->builder, "fail_box") | ||
327 | ); | ||
328 | |||
329 | handle->loading_spinner = GTK_SPINNER( | ||
330 | gtk_builder_get_object(handle->builder, "loading_spinner") | ||
331 | ); | ||
332 | |||
333 | handle->id_drawing_area = GTK_DRAWING_AREA( | ||
334 | gtk_builder_get_object(handle->builder, "id_drawing_area") | ||
335 | ); | ||
336 | |||
337 | handle->id_draw_signal = g_signal_connect( | ||
338 | handle->id_drawing_area, | ||
339 | "draw", | ||
340 | G_CALLBACK(handle_id_drawing_area_draw), | ||
341 | handle | ||
342 | ); | ||
343 | |||
344 | handle->id_entry = GTK_ENTRY( | ||
345 | gtk_builder_get_object(handle->builder, "id_entry") | ||
346 | ); | ||
347 | |||
348 | handle->cancel_button = GTK_BUTTON( | ||
349 | gtk_builder_get_object(handle->builder, "cancel_button") | ||
350 | ); | ||
351 | |||
352 | g_signal_connect( | ||
353 | handle->cancel_button, | ||
354 | "clicked", | ||
355 | G_CALLBACK(handle_cancel_button_click), | ||
356 | handle->dialog | ||
357 | ); | ||
358 | |||
359 | handle->generate_button = GTK_BUTTON( | ||
360 | gtk_builder_get_object(handle->builder, "generate_button") | ||
361 | ); | ||
362 | |||
363 | g_signal_connect( | ||
364 | handle->generate_button, | ||
365 | "clicked", | ||
366 | G_CALLBACK(handle_generate_button_click), | ||
367 | app | ||
368 | ); | ||
369 | |||
370 | handle->copy_button = GTK_BUTTON( | ||
371 | gtk_builder_get_object(handle->builder, "copy_button") | ||
372 | ); | ||
373 | |||
374 | g_signal_connect( | ||
375 | handle->copy_button, | ||
376 | "clicked", | ||
377 | G_CALLBACK(handle_copy_button_click), | ||
378 | app | ||
379 | ); | ||
380 | |||
381 | g_signal_connect( | ||
382 | handle->dialog, | ||
383 | "destroy", | ||
384 | G_CALLBACK(handle_dialog_destroy), | ||
385 | handle | ||
386 | ); | ||
387 | } | ||
388 | |||
389 | |||
390 | void | ||
391 | ui_new_lobby_dialog_cleanup(UI_NEW_LOBBY_Handle *handle) | ||
392 | { | ||
393 | g_signal_handler_disconnect( | ||
394 | handle->id_drawing_area, | ||
395 | handle->id_draw_signal | ||
396 | ); | ||
397 | |||
398 | g_object_unref(handle->builder); | ||
399 | |||
400 | if (handle->qr) | ||
401 | QRcode_free(handle->qr); | ||
402 | |||
403 | memset(handle, 0, sizeof(*handle)); | ||
404 | } | ||