aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--resources/ui.gresource.xml1
-rw-r--r--resources/ui/messenger.ui4
-rw-r--r--resources/ui/new_contact.ui2
-rw-r--r--resources/ui/new_lobby.ui403
-rw-r--r--src/application.h2
-rw-r--r--src/ui/messenger.c94
-rw-r--r--src/ui/messenger.h4
-rw-r--r--src/ui/new_contact.c25
-rw-r--r--src/ui/new_lobby.c404
-rw-r--r--src/ui/new_lobby.h71
11 files changed, 981 insertions, 30 deletions
diff --git a/Makefile b/Makefile
index 0ccef0c..8ca3f30 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,7 @@ SOURCES = messenger_gtk.c\
27 ui/new_account.c\ 27 ui/new_account.c\
28 ui/new_contact.c\ 28 ui/new_contact.c\
29 ui/new_group.c\ 29 ui/new_group.c\
30 ui/new_lobby.c\
30 ui/new_platform.c\ 31 ui/new_platform.c\
31 ui/picker.c\ 32 ui/picker.c\
32 ui/send_file.c\ 33 ui/send_file.c\
diff --git a/resources/ui.gresource.xml b/resources/ui.gresource.xml
index 3a5e629..cbdcf3d 100644
--- a/resources/ui.gresource.xml
+++ b/resources/ui.gresource.xml
@@ -16,6 +16,7 @@
16 <file compressed="true">ui/messenger.ui</file> 16 <file compressed="true">ui/messenger.ui</file>
17 <file compressed="true">ui/new_contact.ui</file> 17 <file compressed="true">ui/new_contact.ui</file>
18 <file compressed="true">ui/new_group.ui</file> 18 <file compressed="true">ui/new_group.ui</file>
19 <file compressed="true">ui/new_lobby.ui</file>
19 <file compressed="true">ui/new_platform.ui</file> 20 <file compressed="true">ui/new_platform.ui</file>
20 <file compressed="true">ui/new_account.ui</file> 21 <file compressed="true">ui/new_account.ui</file>
21 <file compressed="true">ui/picker.ui</file> 22 <file compressed="true">ui/picker.ui</file>
diff --git a/resources/ui/messenger.ui b/resources/ui/messenger.ui
index 2f3cd8f..28dd808 100644
--- a/resources/ui/messenger.ui
+++ b/resources/ui/messenger.ui
@@ -246,7 +246,7 @@ Author: Tobias Frisch
246 </packing> 246 </packing>
247 </child> 247 </child>
248 <child> 248 <child>
249 <object class="GtkButton" id="favourites_button"> 249 <object class="GtkButton" id="lobby_button">
250 <property name="visible">True</property> 250 <property name="visible">True</property>
251 <property name="can-focus">True</property> 251 <property name="can-focus">True</property>
252 <property name="receives-default">True</property> 252 <property name="receives-default">True</property>
@@ -257,7 +257,7 @@ Author: Tobias Frisch
257 <object class="GtkImage"> 257 <object class="GtkImage">
258 <property name="visible">True</property> 258 <property name="visible">True</property>
259 <property name="can-focus">False</property> 259 <property name="can-focus">False</property>
260 <property name="icon-name">user-bookmarks-symbolic</property> 260 <property name="icon-name">dialog-password-symbolic</property>
261 </object> 261 </object>
262 </child> 262 </child>
263 </object> 263 </object>
diff --git a/resources/ui/new_contact.ui b/resources/ui/new_contact.ui
index 8db0635..a4dc25d 100644
--- a/resources/ui/new_contact.ui
+++ b/resources/ui/new_contact.ui
@@ -27,6 +27,8 @@ Author: Tobias Frisch
27 <property name="title" translatable="yes">New Contact</property> 27 <property name="title" translatable="yes">New Contact</property>
28 <property name="modal">True</property> 28 <property name="modal">True</property>
29 <property name="window-position">center-on-parent</property> 29 <property name="window-position">center-on-parent</property>
30 <property name="default-width">400</property>
31 <property name="default-height">350</property>
30 <property name="type-hint">dialog</property> 32 <property name="type-hint">dialog</property>
31 <child internal-child="vbox"> 33 <child internal-child="vbox">
32 <object class="GtkBox"> 34 <object class="GtkBox">
diff --git a/resources/ui/new_lobby.ui b/resources/ui/new_lobby.ui
new file mode 100644
index 0000000..cf37d0c
--- /dev/null
+++ b/resources/ui/new_lobby.ui
@@ -0,0 +1,403 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!-- Generated with glade 3.38.2
3
4Copyright (C) 2022 GNUnet e.V.
5
6GNUnet is free software: you can redistribute it and/or modify it
7under the terms of the GNU Affero General Public License as published
8by the Free Software Foundation, either version 3 of the License,
9or (at your option) any later version.
10
11GNUnet is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14Affero General Public License for more details.
15
16You should have received a copy of the GNU Affero General Public License
17along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19SPDX-License-Identifier: AGPL3.0-or-later
20Author: Tobias Frisch
21
22-->
23<interface>
24 <requires lib="gtk+" version="3.24"/>
25 <requires lib="libhandy" version="1.2"/>
26 <object class="GtkListStore" id="delay_store">
27 <columns>
28 <!-- column-name name -->
29 <column type="gchararray"/>
30 <!-- column-name value -->
31 <column type="gulong"/>
32 </columns>
33 <data>
34 <row>
35 <col id="0" translatable="yes">Off</col>
36 <col id="1">0</col>
37 </row>
38 <row>
39 <col id="0" translatable="yes">4 weeks</col>
40 <col id="1">2419200</col>
41 </row>
42 <row>
43 <col id="0" translatable="yes">1 week</col>
44 <col id="1">604800</col>
45 </row>
46 <row>
47 <col id="0" translatable="yes">1 day</col>
48 <col id="1">86400</col>
49 </row>
50 <row>
51 <col id="0" translatable="yes">8 hours</col>
52 <col id="1">28800</col>
53 </row>
54 <row>
55 <col id="0" translatable="yes">1 hour</col>
56 <col id="1">3600</col>
57 </row>
58 <row>
59 <col id="0" translatable="yes">5 minutes</col>
60 <col id="1">300</col>
61 </row>
62 <row>
63 <col id="0" translatable="yes">30 seconds</col>
64 <col id="1">30</col>
65 </row>
66 </data>
67 </object>
68 <object class="GtkDialog" id="new_lobby_dialog">
69 <property name="can-focus">False</property>
70 <property name="title" translatable="yes">New Lobby</property>
71 <property name="modal">True</property>
72 <property name="window-position">center-on-parent</property>
73 <property name="default-height">450</property>
74 <property name="type-hint">dialog</property>
75 <child internal-child="vbox">
76 <object class="GtkBox">
77 <property name="can-focus">False</property>
78 <property name="orientation">vertical</property>
79 <property name="spacing">2</property>
80 <child internal-child="action_area">
81 <object class="GtkButtonBox">
82 <property name="can-focus">False</property>
83 <property name="layout-style">end</property>
84 <child>
85 <object class="GtkButton" id="cancel_button">
86 <property name="label" translatable="yes">Cancel</property>
87 <property name="visible">True</property>
88 <property name="can-focus">True</property>
89 <property name="receives-default">True</property>
90 </object>
91 <packing>
92 <property name="expand">True</property>
93 <property name="fill">True</property>
94 <property name="position">0</property>
95 </packing>
96 </child>
97 <child>
98 <object class="GtkButton" id="generate_button">
99 <property name="label" translatable="yes">Generate</property>
100 <property name="visible">True</property>
101 <property name="can-focus">True</property>
102 <property name="receives-default">True</property>
103 </object>
104 <packing>
105 <property name="expand">True</property>
106 <property name="fill">True</property>
107 <property name="position">1</property>
108 </packing>
109 </child>
110 <child>
111 <object class="GtkButton" id="copy_button">
112 <property name="label" translatable="yes">Copy</property>
113 <property name="sensitive">False</property>
114 <property name="can-focus">True</property>
115 <property name="receives-default">True</property>
116 </object>
117 <packing>
118 <property name="expand">True</property>
119 <property name="fill">True</property>
120 <property name="position">2</property>
121 </packing>
122 </child>
123 </object>
124 <packing>
125 <property name="expand">False</property>
126 <property name="fill">False</property>
127 <property name="position">0</property>
128 </packing>
129 </child>
130 <child>
131 <object class="GtkBox">
132 <property name="visible">True</property>
133 <property name="can-focus">False</property>
134 <property name="orientation">vertical</property>
135 <child>
136 <object class="GtkInfoBar" id="warning_info_bar">
137 <property name="visible">True</property>
138 <property name="can-focus">False</property>
139 <property name="message-type">warning</property>
140 <property name="show-close-button">True</property>
141 <child internal-child="action_area">
142 <object class="GtkButtonBox">
143 <property name="can-focus">False</property>
144 <property name="halign">center</property>
145 <property name="valign">center</property>
146 <property name="layout-style">expand</property>
147 <child>
148 <placeholder/>
149 </child>
150 </object>
151 <packing>
152 <property name="expand">False</property>
153 <property name="fill">False</property>
154 <property name="position">0</property>
155 </packing>
156 </child>
157 <child internal-child="content_area">
158 <object class="GtkBox">
159 <property name="can-focus">False</property>
160 <property name="border-width">8</property>
161 <property name="spacing">16</property>
162 <child>
163 <object class="GtkLabel">
164 <property name="visible">True</property>
165 <property name="can-focus">False</property>
166 <property name="label" translatable="yes">Beware that you don't reuse lobbies to establish private chats with only one of your contacts!</property>
167 <property name="wrap">True</property>
168 <property name="wrap-mode">word-char</property>
169 </object>
170 <packing>
171 <property name="expand">False</property>
172 <property name="fill">True</property>
173 <property name="position">0</property>
174 </packing>
175 </child>
176 </object>
177 <packing>
178 <property name="expand">False</property>
179 <property name="fill">True</property>
180 <property name="position">0</property>
181 </packing>
182 </child>
183 </object>
184 <packing>
185 <property name="expand">False</property>
186 <property name="fill">True</property>
187 <property name="position">0</property>
188 </packing>
189 </child>
190 <child>
191 <object class="GtkScrolledWindow">
192 <property name="height-request">200</property>
193 <property name="visible">True</property>
194 <property name="can-focus">True</property>
195 <property name="hscrollbar-policy">never</property>
196 <property name="shadow-type">in</property>
197 <child>
198 <object class="GtkViewport">
199 <property name="visible">True</property>
200 <property name="can-focus">False</property>
201 <child>
202 <object class="GtkStack" id="new_lobby_stack">
203 <property name="visible">True</property>
204 <property name="can-focus">False</property>
205 <child>
206 <object class="GtkBox" id="generate_box">
207 <property name="visible">True</property>
208 <property name="can-focus">False</property>
209 <property name="halign">center</property>
210 <property name="valign">center</property>
211 <property name="border-width">8</property>
212 <property name="orientation">vertical</property>
213 <property name="spacing">4</property>
214 <child>
215 <object class="HdyAvatar">
216 <property name="visible">True</property>
217 <property name="can-focus">False</property>
218 <property name="halign">center</property>
219 <property name="margin-top">8</property>
220 <property name="margin-bottom">8</property>
221 <property name="icon-name">dialog-password-symbolic</property>
222 <property name="size">128</property>
223 </object>
224 <packing>
225 <property name="expand">False</property>
226 <property name="fill">True</property>
227 <property name="position">0</property>
228 </packing>
229 </child>
230 <child>
231 <object class="GtkLabel">
232 <property name="visible">True</property>
233 <property name="can-focus">False</property>
234 <property name="label" translatable="yes">Delay until new lobby expires:</property>
235 <property name="xalign">0</property>
236 </object>
237 <packing>
238 <property name="expand">False</property>
239 <property name="fill">True</property>
240 <property name="position">1</property>
241 </packing>
242 </child>
243 <child>
244 <object class="GtkComboBox" id="expiration_combo_box">
245 <property name="width-request">250</property>
246 <property name="visible">True</property>
247 <property name="can-focus">False</property>
248 <property name="model">delay_store</property>
249 <property name="active">0</property>
250 <child>
251 <object class="GtkCellRendererText"/>
252 <attributes>
253 <attribute name="text">0</attribute>
254 </attributes>
255 </child>
256 </object>
257 <packing>
258 <property name="expand">False</property>
259 <property name="fill">True</property>
260 <property name="position">2</property>
261 </packing>
262 </child>
263 </object>
264 <packing>
265 <property name="name">page_generate</property>
266 </packing>
267 </child>
268 <child>
269 <object class="GtkBox" id="copy_box">
270 <property name="visible">True</property>
271 <property name="can-focus">False</property>
272 <property name="halign">center</property>
273 <property name="valign">center</property>
274 <property name="orientation">vertical</property>
275 <child>
276 <object class="GtkStack" id="preview_stack">
277 <property name="visible">True</property>
278 <property name="can-focus">False</property>
279 <child>
280 <object class="GtkBox" id="fail_box">
281 <property name="visible">True</property>
282 <property name="can-focus">False</property>
283 <property name="valign">center</property>
284 <property name="orientation">vertical</property>
285 <property name="spacing">8</property>
286 <child>
287 <object class="GtkImage">
288 <property name="visible">True</property>
289 <property name="can-focus">False</property>
290 <property name="pixel-size">64</property>
291 <property name="icon-name">action-unavailable-symbolic</property>
292 <property name="icon_size">3</property>
293 </object>
294 <packing>
295 <property name="expand">False</property>
296 <property name="fill">True</property>
297 <property name="position">0</property>
298 </packing>
299 </child>
300 <child>
301 <object class="GtkLabel">
302 <property name="visible">True</property>
303 <property name="can-focus">False</property>
304 <property name="label" translatable="yes">Generation of new lobby failed!</property>
305 </object>
306 <packing>
307 <property name="expand">False</property>
308 <property name="fill">True</property>
309 <property name="position">1</property>
310 </packing>
311 </child>
312 </object>
313 <packing>
314 <property name="name">page_fail</property>
315 </packing>
316 </child>
317 <child>
318 <object class="GtkSpinner" id="loading_spinner">
319 <property name="visible">True</property>
320 <property name="can-focus">False</property>
321 <property name="active">True</property>
322 </object>
323 <packing>
324 <property name="name">page_loading</property>
325 <property name="position">1</property>
326 </packing>
327 </child>
328 <child>
329 <object class="GtkDrawingArea" id="id_drawing_area">
330 <property name="height-request">250</property>
331 <property name="visible">True</property>
332 <property name="can-focus">False</property>
333 <property name="margin-start">8</property>
334 <property name="margin-end">8</property>
335 <property name="margin-top">8</property>
336 <property name="margin-bottom">8</property>
337 </object>
338 <packing>
339 <property name="name">page_drawing</property>
340 <property name="position">2</property>
341 </packing>
342 </child>
343 </object>
344 <packing>
345 <property name="expand">False</property>
346 <property name="fill">True</property>
347 <property name="position">0</property>
348 </packing>
349 </child>
350 <child>
351 <object class="GtkLabel">
352 <property name="visible">True</property>
353 <property name="can-focus">False</property>
354 <property name="label" translatable="yes">ID:</property>
355 <property name="xalign">0</property>
356 </object>
357 <packing>
358 <property name="expand">False</property>
359 <property name="fill">True</property>
360 <property name="position">1</property>
361 </packing>
362 </child>
363 <child>
364 <object class="GtkEntry" id="id_entry">
365 <property name="width-request">250</property>
366 <property name="visible">True</property>
367 <property name="can-focus">True</property>
368 <property name="editable">False</property>
369 </object>
370 <packing>
371 <property name="expand">False</property>
372 <property name="fill">True</property>
373 <property name="position">2</property>
374 </packing>
375 </child>
376 </object>
377 <packing>
378 <property name="name">page_copy</property>
379 <property name="position">1</property>
380 </packing>
381 </child>
382 </object>
383 </child>
384 </object>
385 </child>
386 </object>
387 <packing>
388 <property name="expand">True</property>
389 <property name="fill">True</property>
390 <property name="position">1</property>
391 </packing>
392 </child>
393 </object>
394 <packing>
395 <property name="expand">True</property>
396 <property name="fill">True</property>
397 <property name="position">1</property>
398 </packing>
399 </child>
400 </object>
401 </child>
402 </object>
403</interface>
diff --git a/src/application.h b/src/application.h
index 3520f09..d678e1b 100644
--- a/src/application.h
+++ b/src/application.h
@@ -38,6 +38,7 @@
38#include "ui/new_account.h" 38#include "ui/new_account.h"
39#include "ui/new_contact.h" 39#include "ui/new_contact.h"
40#include "ui/new_group.h" 40#include "ui/new_group.h"
41#include "ui/new_lobby.h"
41#include "ui/new_platform.h" 42#include "ui/new_platform.h"
42#include "ui/send_file.h" 43#include "ui/send_file.h"
43#include "ui/settings.h" 44#include "ui/settings.h"
@@ -87,6 +88,7 @@ typedef struct MESSENGER_Application
87 88
88 UI_NEW_CONTACT_Handle new_contact; 89 UI_NEW_CONTACT_Handle new_contact;
89 UI_NEW_GROUP_Handle new_group; 90 UI_NEW_GROUP_Handle new_group;
91 UI_NEW_LOBBY_Handle new_lobby;
90 UI_NEW_PLATFORM_Handle new_platform; 92 UI_NEW_PLATFORM_Handle new_platform;
91 UI_NEW_ACCOUNT_Handle new_account; 93 UI_NEW_ACCOUNT_Handle new_account;
92 94
diff --git a/src/ui/messenger.c b/src/ui/messenger.c
index 246bb9c..270360d 100644
--- a/src/ui/messenger.c
+++ b/src/ui/messenger.c
@@ -31,6 +31,8 @@
31#include "contacts.h" 31#include "contacts.h"
32#include "message.h" 32#include "message.h"
33#include "new_contact.h" 33#include "new_contact.h"
34#include "new_group.h"
35#include "new_lobby.h"
34#include "new_platform.h" 36#include "new_platform.h"
35#include "settings.h" 37#include "settings.h"
36 38
@@ -58,6 +60,19 @@ handle_flap_via_button_click(UNUSED GtkButton* button,
58} 60}
59 61
60static void 62static void
63handle_lobby_button_click(UNUSED GtkButton* button,
64 gpointer user_data)
65{
66 MESSENGER_Application *app = (MESSENGER_Application*) user_data;
67
68 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE);
69
70 ui_new_lobby_dialog_init(app, &(app->ui.new_lobby));
71
72 gtk_widget_show(GTK_WIDGET(app->ui.new_lobby.dialog));
73}
74
75static void
61_switch_details_revealer_visibility(UI_MESSENGER_Handle *handle, 76_switch_details_revealer_visibility(UI_MESSENGER_Handle *handle,
62 gboolean state) 77 gboolean state)
63{ 78{
@@ -264,6 +279,25 @@ handle_main_window_destroy(UNUSED GtkWidget *window,
264 application_exit(app, MESSENGER_QUIT); 279 application_exit(app, MESSENGER_QUIT);
265} 280}
266 281
282static void
283_switch_accounts_listbox_connection(MESSENGER_Application *app,
284 UI_MESSENGER_Handle *handle,
285 gboolean enabled)
286{
287 if (enabled)
288 handle->accounts_signal = g_signal_connect(
289 handle->accounts_listbox,
290 "row-activated",
291 G_CALLBACK(handle_accounts_listbox_row_activated),
292 app
293 );
294 else
295 g_signal_handler_disconnect(
296 handle->accounts_listbox,
297 handle->accounts_signal
298 );
299}
300
267void 301void
268ui_messenger_init(MESSENGER_Application *app, 302ui_messenger_init(MESSENGER_Application *app,
269 UI_MESSENGER_Handle *handle) 303 UI_MESSENGER_Handle *handle)
@@ -342,8 +376,15 @@ ui_messenger_init(MESSENGER_Application *app,
342 handle->flap_user_details 376 handle->flap_user_details
343 ); 377 );
344 378
345 handle->favourites_button = GTK_BUTTON( 379 handle->lobby_button = GTK_BUTTON(
346 gtk_builder_get_object(handle->builder, "favourites_button") 380 gtk_builder_get_object(handle->builder, "lobby_button")
381 );
382
383 g_signal_connect(
384 handle->lobby_button,
385 "clicked",
386 G_CALLBACK(handle_lobby_button_click),
387 app
347 ); 388 );
348 389
349 handle->account_details_button = GTK_BUTTON( 390 handle->account_details_button = GTK_BUTTON(
@@ -373,12 +414,7 @@ ui_messenger_init(MESSENGER_Application *app,
373 gtk_builder_get_object(handle->builder, "add_account_listbox_row") 414 gtk_builder_get_object(handle->builder, "add_account_listbox_row")
374 ); 415 );
375 416
376 g_signal_connect( 417 _switch_accounts_listbox_connection(app, handle, TRUE);
377 handle->accounts_listbox,
378 "row-activated",
379 G_CALLBACK(handle_accounts_listbox_row_activated),
380 app
381 );
382 418
383 handle->new_contact_button = GTK_BUTTON( 419 handle->new_contact_button = GTK_BUTTON(
384 gtk_builder_get_object(handle->builder, "new_contact_button") 420 gtk_builder_get_object(handle->builder, "new_contact_button")
@@ -522,6 +558,22 @@ _messenger_iterate_accounts(void *cls,
522 return GNUNET_YES; 558 return GNUNET_YES;
523} 559}
524 560
561static void
562_clear_accounts_listbox(GtkWidget *widget,
563 gpointer data)
564{
565 GtkListBoxRow *row = GTK_LIST_BOX_ROW(widget);
566 GtkListBox *listbox = GTK_LIST_BOX(data);
567
568 if ((!row) || (!listbox) || (!gtk_list_box_row_get_selectable(row)))
569 return;
570
571 gtk_container_remove(
572 GTK_CONTAINER(listbox),
573 widget
574 );
575}
576
525void 577void
526ui_messenger_refresh(MESSENGER_Application *app, 578ui_messenger_refresh(MESSENGER_Application *app,
527 UI_MESSENGER_Handle *handle) 579 UI_MESSENGER_Handle *handle)
@@ -529,31 +581,21 @@ ui_messenger_refresh(MESSENGER_Application *app,
529 if (!(handle->accounts_listbox)) 581 if (!(handle->accounts_listbox))
530 return; 582 return;
531 583
532 GList *list = gtk_container_get_children( 584 _switch_accounts_listbox_connection(app, handle, FALSE);
533 GTK_CONTAINER(handle->accounts_listbox)
534 );
535
536 while (list)
537 {
538 GtkListBoxRow *row = GTK_LIST_BOX_ROW(list->data);
539
540 if ((!row) || (!gtk_list_box_row_get_selectable(row)))
541 goto skip_row;
542 585
543 gtk_container_remove( 586 gtk_container_foreach(
544 GTK_CONTAINER(handle->accounts_listbox), 587 GTK_CONTAINER(handle->accounts_listbox),
545 GTK_WIDGET(row) 588 _clear_accounts_listbox,
546 ); 589 handle->accounts_listbox
547 590 );
548 skip_row:
549 list = list->next;
550 }
551 591
552 GNUNET_CHAT_iterate_accounts( 592 GNUNET_CHAT_iterate_accounts(
553 app->chat.messenger.handle, 593 app->chat.messenger.handle,
554 _messenger_iterate_accounts, 594 _messenger_iterate_accounts,
555 app 595 app
556 ); 596 );
597
598 _switch_accounts_listbox_connection(app, handle, TRUE);
557} 599}
558 600
559gboolean 601gboolean
diff --git a/src/ui/messenger.h b/src/ui/messenger.h
index c23a27d..ef1ae60 100644
--- a/src/ui/messenger.h
+++ b/src/ui/messenger.h
@@ -52,7 +52,7 @@ typedef struct UI_MESSENGER_Handle
52 GtkLabel *profile_key_label; 52 GtkLabel *profile_key_label;
53 53
54 GtkButton *hide_user_details_button; 54 GtkButton *hide_user_details_button;
55 GtkButton *favourites_button; 55 GtkButton *lobby_button;
56 GtkButton *account_details_button; 56 GtkButton *account_details_button;
57 GtkImage *account_details_symbol; 57 GtkImage *account_details_symbol;
58 58
@@ -72,6 +72,8 @@ typedef struct UI_MESSENGER_Handle
72 72
73 GtkStack *chats_stack; 73 GtkStack *chats_stack;
74 GtkWidget *no_chat_box; 74 GtkWidget *no_chat_box;
75
76 gulong accounts_signal;
75} UI_MESSENGER_Handle; 77} UI_MESSENGER_Handle;
76 78
77void 79void
diff --git a/src/ui/new_contact.c b/src/ui/new_contact.c
index 8188740..6438331 100644
--- a/src/ui/new_contact.c
+++ b/src/ui/new_contact.c
@@ -40,8 +40,28 @@ handle_confirm_button_click(UNUSED GtkButton *button,
40{ 40{
41 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 41 MESSENGER_Application *app = (MESSENGER_Application*) user_data;
42 42
43 // TODO: Add new contact 43 const gint id_length = gtk_entry_get_text_length(app->ui.new_contact.id_entry);
44 const gchar *id_text = gtk_entry_get_text(app->ui.new_contact.id_entry);
44 45
46 if (id_length <= 0)
47 goto close_dialog;
48
49 gchar *emsg = NULL;
50 struct GNUNET_CHAT_Uri *uri = GNUNET_CHAT_uri_parse(id_text, &emsg);
51
52 if (uri)
53 {
54 GNUNET_CHAT_lobby_join(app->chat.messenger.handle, uri);
55 GNUNET_CHAT_uri_destroy(uri);
56 }
57
58 if (emsg)
59 {
60 printf("ERROR: %s\n", emsg);
61 GNUNET_free(emsg);
62 }
63
64close_dialog:
45 gtk_window_close(GTK_WINDOW(app->ui.new_contact.dialog)); 65 gtk_window_close(GTK_WINDOW(app->ui.new_contact.dialog));
46} 66}
47 67
@@ -61,6 +81,9 @@ handle_id_drawing_area_draw(GtkWidget* drawing_area,
61 81
62 GtkStyleContext* context = gtk_widget_get_style_context(drawing_area); 82 GtkStyleContext* context = gtk_widget_get_style_context(drawing_area);
63 83
84 if (!context)
85 return FALSE;
86
64 const guint width = gtk_widget_get_allocated_width(drawing_area); 87 const guint width = gtk_widget_get_allocated_width(drawing_area);
65 const guint height = gtk_widget_get_allocated_height(drawing_area); 88 const guint height = gtk_widget_get_allocated_height(drawing_area);
66 89
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
29static void
30handle_warning_info_bar_close(GtkInfoBar *info_bar,
31 UNUSED gpointer user_data)
32{
33 gtk_info_bar_set_revealed(info_bar, FALSE);
34}
35
36static void
37handle_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
44static void
45handle_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
52void
53handle_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
108static void
109handle_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
153static void
154handle_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
170static void
171handle_dialog_destroy(UNUSED GtkWidget *window,
172 gpointer user_data)
173{
174 ui_new_lobby_dialog_cleanup((UI_NEW_LOBBY_Handle*) user_data);
175}
176
177static gboolean
178handle_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
270void
271ui_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
390void
391ui_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}
diff --git a/src/ui/new_lobby.h b/src/ui/new_lobby.h
new file mode 100644
index 0000000..9d100b6
--- /dev/null
+++ b/src/ui/new_lobby.h
@@ -0,0 +1,71 @@
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.h
23 */
24
25#ifndef UI_NEW_LOBBY_H_
26#define UI_NEW_LOBBY_H_
27
28#include "messenger.h"
29
30#include <cairo/cairo.h>
31#include <gdk/gdkpixbuf.h>
32#include <qrencode.h>
33
34typedef struct UI_NEW_LOBBY_Handle
35{
36 GtkBuilder *builder;
37 GtkDialog *dialog;
38
39 GtkInfoBar *warning_info_bar;
40
41 GtkStack *stack;
42 GtkWidget *generate_box;
43 GtkWidget *copy_box;
44
45 GtkComboBox *expiration_combo_box;
46
47 GtkStack *preview_stack;
48 GtkWidget *fail_box;
49
50 GtkSpinner *loading_spinner;
51
52 GtkDrawingArea *id_drawing_area;
53 GtkEntry *id_entry;
54
55 gulong id_draw_signal;
56
57 GtkButton *cancel_button;
58 GtkButton *generate_button;
59 GtkButton *copy_button;
60
61 QRcode *qr;
62} UI_NEW_LOBBY_Handle;
63
64void
65ui_new_lobby_dialog_init(MESSENGER_Application *app,
66 UI_NEW_LOBBY_Handle *handle);
67
68void
69ui_new_lobby_dialog_cleanup(UI_NEW_LOBBY_Handle *handle);
70
71#endif /* UI_NEW_LOBBY_H_ */