From 85b243c149d96ac88c5afd20993bbe18c50e16ca Mon Sep 17 00:00:00 2001 From: TheJackiMonster Date: Sat, 12 Mar 2022 22:59:40 +0100 Subject: Implemented QR generation and usage of lobbies Signed-off-by: TheJackiMonster --- Makefile | 1 + resources/ui.gresource.xml | 1 + resources/ui/messenger.ui | 4 +- resources/ui/new_contact.ui | 2 + resources/ui/new_lobby.ui | 403 +++++++++++++++++++++++++++++++++++++++++++ src/application.h | 2 + src/ui/messenger.c | 94 ++++++++--- src/ui/messenger.h | 4 +- src/ui/new_contact.c | 25 ++- src/ui/new_lobby.c | 404 ++++++++++++++++++++++++++++++++++++++++++++ src/ui/new_lobby.h | 71 ++++++++ 11 files changed, 981 insertions(+), 30 deletions(-) create mode 100644 resources/ui/new_lobby.ui create mode 100644 src/ui/new_lobby.c create mode 100644 src/ui/new_lobby.h diff --git a/Makefile b/Makefile index 0ccef0c..8ca3f30 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ SOURCES = messenger_gtk.c\ ui/new_account.c\ ui/new_contact.c\ ui/new_group.c\ + ui/new_lobby.c\ ui/new_platform.c\ ui/picker.c\ 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 @@ ui/messenger.ui ui/new_contact.ui ui/new_group.ui + ui/new_lobby.ui ui/new_platform.ui ui/new_account.ui ui/picker.ui 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 - + True True True @@ -257,7 +257,7 @@ Author: Tobias Frisch True False - user-bookmarks-symbolic + dialog-password-symbolic 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 New Contact True center-on-parent + 400 + 350 dialog 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 @@ + + + + + + + + + + + + + + + Off + 0 + + + 4 weeks + 2419200 + + + 1 week + 604800 + + + 1 day + 86400 + + + 8 hours + 28800 + + + 1 hour + 3600 + + + 5 minutes + 300 + + + 30 seconds + 30 + + + + + False + New Lobby + True + center-on-parent + 450 + dialog + + + False + vertical + 2 + + + False + end + + + Cancel + True + True + True + + + True + True + 0 + + + + + Generate + True + True + True + + + True + True + 1 + + + + + Copy + False + True + True + + + True + True + 2 + + + + + False + False + 0 + + + + + True + False + vertical + + + True + False + warning + True + + + False + center + center + expand + + + + + + False + False + 0 + + + + + False + 8 + 16 + + + True + False + Beware that you don't reuse lobbies to establish private chats with only one of your contacts! + True + word-char + + + False + True + 0 + + + + + False + True + 0 + + + + + False + True + 0 + + + + + 200 + True + True + never + in + + + True + False + + + True + False + + + True + False + center + center + 8 + vertical + 4 + + + True + False + center + 8 + 8 + dialog-password-symbolic + 128 + + + False + True + 0 + + + + + True + False + Delay until new lobby expires: + 0 + + + False + True + 1 + + + + + 250 + True + False + delay_store + 0 + + + + 0 + + + + + False + True + 2 + + + + + page_generate + + + + + True + False + center + center + vertical + + + True + False + + + True + False + center + vertical + 8 + + + True + False + 64 + action-unavailable-symbolic + 3 + + + False + True + 0 + + + + + True + False + Generation of new lobby failed! + + + False + True + 1 + + + + + page_fail + + + + + True + False + True + + + page_loading + 1 + + + + + 250 + True + False + 8 + 8 + 8 + 8 + + + page_drawing + 2 + + + + + False + True + 0 + + + + + True + False + ID: + 0 + + + False + True + 1 + + + + + 250 + True + True + False + + + False + True + 2 + + + + + page_copy + 1 + + + + + + + + + True + True + 1 + + + + + True + True + 1 + + + + + + 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 @@ #include "ui/new_account.h" #include "ui/new_contact.h" #include "ui/new_group.h" +#include "ui/new_lobby.h" #include "ui/new_platform.h" #include "ui/send_file.h" #include "ui/settings.h" @@ -87,6 +88,7 @@ typedef struct MESSENGER_Application UI_NEW_CONTACT_Handle new_contact; UI_NEW_GROUP_Handle new_group; + UI_NEW_LOBBY_Handle new_lobby; UI_NEW_PLATFORM_Handle new_platform; UI_NEW_ACCOUNT_Handle new_account; 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 @@ #include "contacts.h" #include "message.h" #include "new_contact.h" +#include "new_group.h" +#include "new_lobby.h" #include "new_platform.h" #include "settings.h" @@ -57,6 +59,19 @@ handle_flap_via_button_click(UNUSED GtkButton* button, g_idle_add(G_SOURCE_FUNC(_flap_reveal_switch), user_data); } +static void +handle_lobby_button_click(UNUSED GtkButton* button, + gpointer user_data) +{ + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); + + ui_new_lobby_dialog_init(app, &(app->ui.new_lobby)); + + gtk_widget_show(GTK_WIDGET(app->ui.new_lobby.dialog)); +} + static void _switch_details_revealer_visibility(UI_MESSENGER_Handle *handle, gboolean state) @@ -264,6 +279,25 @@ handle_main_window_destroy(UNUSED GtkWidget *window, application_exit(app, MESSENGER_QUIT); } +static void +_switch_accounts_listbox_connection(MESSENGER_Application *app, + UI_MESSENGER_Handle *handle, + gboolean enabled) +{ + if (enabled) + handle->accounts_signal = g_signal_connect( + handle->accounts_listbox, + "row-activated", + G_CALLBACK(handle_accounts_listbox_row_activated), + app + ); + else + g_signal_handler_disconnect( + handle->accounts_listbox, + handle->accounts_signal + ); +} + void ui_messenger_init(MESSENGER_Application *app, UI_MESSENGER_Handle *handle) @@ -342,8 +376,15 @@ ui_messenger_init(MESSENGER_Application *app, handle->flap_user_details ); - handle->favourites_button = GTK_BUTTON( - gtk_builder_get_object(handle->builder, "favourites_button") + handle->lobby_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "lobby_button") + ); + + g_signal_connect( + handle->lobby_button, + "clicked", + G_CALLBACK(handle_lobby_button_click), + app ); handle->account_details_button = GTK_BUTTON( @@ -373,12 +414,7 @@ ui_messenger_init(MESSENGER_Application *app, gtk_builder_get_object(handle->builder, "add_account_listbox_row") ); - g_signal_connect( - handle->accounts_listbox, - "row-activated", - G_CALLBACK(handle_accounts_listbox_row_activated), - app - ); + _switch_accounts_listbox_connection(app, handle, TRUE); handle->new_contact_button = GTK_BUTTON( gtk_builder_get_object(handle->builder, "new_contact_button") @@ -522,6 +558,22 @@ _messenger_iterate_accounts(void *cls, return GNUNET_YES; } +static void +_clear_accounts_listbox(GtkWidget *widget, + gpointer data) +{ + GtkListBoxRow *row = GTK_LIST_BOX_ROW(widget); + GtkListBox *listbox = GTK_LIST_BOX(data); + + if ((!row) || (!listbox) || (!gtk_list_box_row_get_selectable(row))) + return; + + gtk_container_remove( + GTK_CONTAINER(listbox), + widget + ); +} + void ui_messenger_refresh(MESSENGER_Application *app, UI_MESSENGER_Handle *handle) @@ -529,31 +581,21 @@ ui_messenger_refresh(MESSENGER_Application *app, if (!(handle->accounts_listbox)) return; - GList *list = gtk_container_get_children( - GTK_CONTAINER(handle->accounts_listbox) - ); - - while (list) - { - GtkListBoxRow *row = GTK_LIST_BOX_ROW(list->data); - - if ((!row) || (!gtk_list_box_row_get_selectable(row))) - goto skip_row; + _switch_accounts_listbox_connection(app, handle, FALSE); - gtk_container_remove( - GTK_CONTAINER(handle->accounts_listbox), - GTK_WIDGET(row) - ); - - skip_row: - list = list->next; - } + gtk_container_foreach( + GTK_CONTAINER(handle->accounts_listbox), + _clear_accounts_listbox, + handle->accounts_listbox + ); GNUNET_CHAT_iterate_accounts( app->chat.messenger.handle, _messenger_iterate_accounts, app ); + + _switch_accounts_listbox_connection(app, handle, TRUE); } gboolean 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 GtkLabel *profile_key_label; GtkButton *hide_user_details_button; - GtkButton *favourites_button; + GtkButton *lobby_button; GtkButton *account_details_button; GtkImage *account_details_symbol; @@ -72,6 +72,8 @@ typedef struct UI_MESSENGER_Handle GtkStack *chats_stack; GtkWidget *no_chat_box; + + gulong accounts_signal; } UI_MESSENGER_Handle; void 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, { MESSENGER_Application *app = (MESSENGER_Application*) user_data; - // TODO: Add new contact + const gint id_length = gtk_entry_get_text_length(app->ui.new_contact.id_entry); + const gchar *id_text = gtk_entry_get_text(app->ui.new_contact.id_entry); + if (id_length <= 0) + goto close_dialog; + + gchar *emsg = NULL; + struct GNUNET_CHAT_Uri *uri = GNUNET_CHAT_uri_parse(id_text, &emsg); + + if (uri) + { + GNUNET_CHAT_lobby_join(app->chat.messenger.handle, uri); + GNUNET_CHAT_uri_destroy(uri); + } + + if (emsg) + { + printf("ERROR: %s\n", emsg); + GNUNET_free(emsg); + } + +close_dialog: gtk_window_close(GTK_WINDOW(app->ui.new_contact.dialog)); } @@ -61,6 +81,9 @@ handle_id_drawing_area_draw(GtkWidget* drawing_area, GtkStyleContext* context = gtk_widget_get_style_context(drawing_area); + if (!context) + return FALSE; + const guint width = gtk_widget_get_allocated_width(drawing_area); const guint height = gtk_widget_get_allocated_height(drawing_area); 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 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file ui/new_lobby.c + */ + +#include "new_lobby.h" + +#include "../application.h" + +static void +handle_warning_info_bar_close(GtkInfoBar *info_bar, + UNUSED gpointer user_data) +{ + gtk_info_bar_set_revealed(info_bar, FALSE); +} + +static void +handle_warning_info_bar_response(GtkInfoBar *info_bar, + UNUSED int response_id, + UNUSED gpointer user_data) +{ + gtk_info_bar_set_revealed(info_bar, FALSE); +} + +static void +handle_cancel_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + GtkDialog *dialog = GTK_DIALOG(user_data); + gtk_window_close(GTK_WINDOW(dialog)); +} + +void +handle_lobby_opened_and_uri_generated(void *cls, + const struct GNUNET_CHAT_Uri *uri) +{ + MESSENGER_Application *app = (MESSENGER_Application*) cls; + + if (app->ui.new_lobby.qr) + QRcode_free(app->ui.new_lobby.qr); + + if (!uri) + { + if (app->ui.new_lobby.preview_stack) + gtk_stack_set_visible_child( + app->ui.new_lobby.preview_stack, + app->ui.new_lobby.fail_box + ); + + app->ui.new_lobby.qr = NULL; + return; + } + + gchar *uri_string = GNUNET_CHAT_uri_to_string(uri); + + app->ui.new_lobby.qr = QRcode_encodeString( + uri_string, + 0, + QR_ECLEVEL_L, + QR_MODE_8, + 0 + ); + + if (app->ui.new_lobby.id_drawing_area) + gtk_widget_queue_draw(GTK_WIDGET(app->ui.new_lobby.id_drawing_area)); + + if (app->ui.new_lobby.preview_stack) + gtk_stack_set_visible_child( + app->ui.new_lobby.preview_stack, + GTK_WIDGET(app->ui.new_lobby.id_drawing_area) + ); + + if (app->ui.new_lobby.id_entry) + gtk_entry_set_text(app->ui.new_lobby.id_entry, uri_string); + + GNUNET_free(uri_string); + + if (!(app->ui.new_lobby.id_entry)) + return; + + const gint id_length = gtk_entry_get_text_length(app->ui.new_lobby.id_entry); + + gtk_widget_set_sensitive( + GTK_WIDGET(app->ui.new_lobby.copy_button), + id_length > 0? TRUE : FALSE + ); +} + +static void +handle_generate_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + GtkTreeModel *model = gtk_combo_box_get_model( + app->ui.new_lobby.expiration_combo_box + ); + + gulong delay = 0; + + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter(app->ui.new_lobby.expiration_combo_box, + &iter)) + gtk_tree_model_get(model, &iter, 1, &delay, -1); + + struct GNUNET_TIME_Relative expiration = GNUNET_TIME_relative_multiply( + GNUNET_TIME_relative_get_second_(), + delay + ); + + gtk_stack_set_visible_child( + app->ui.new_lobby.preview_stack, + GTK_WIDGET(app->ui.new_lobby.loading_spinner) + ); + + gtk_stack_set_visible_child( + app->ui.new_lobby.stack, + app->ui.new_lobby.copy_box + ); + + gtk_widget_set_sensitive(GTK_WIDGET(app->ui.new_lobby.copy_button), FALSE); + + gtk_widget_set_visible(GTK_WIDGET(app->ui.new_lobby.generate_button), FALSE); + gtk_widget_set_visible(GTK_WIDGET(app->ui.new_lobby.copy_button), TRUE); + + GNUNET_CHAT_lobby_open( + app->chat.messenger.handle, + expiration, + handle_lobby_opened_and_uri_generated, + app + ); +} + +static void +handle_copy_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + const gint id_length = gtk_entry_get_text_length(app->ui.new_lobby.id_entry); + const gchar *id_text = gtk_entry_get_text(app->ui.new_lobby.id_entry); + + GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + if ((clipboard) && (id_length > 0)) + gtk_clipboard_set_text(clipboard, id_text, id_length); + + gtk_window_close(GTK_WINDOW(app->ui.new_lobby.dialog)); +} + +static void +handle_dialog_destroy(UNUSED GtkWidget *window, + gpointer user_data) +{ + ui_new_lobby_dialog_cleanup((UI_NEW_LOBBY_Handle*) user_data); +} + +static gboolean +handle_id_drawing_area_draw(GtkWidget* drawing_area, + cairo_t* cairo, + gpointer user_data) +{ + UI_NEW_LOBBY_Handle *handle = (UI_NEW_LOBBY_Handle*) user_data; + + GtkStyleContext* context = gtk_widget_get_style_context(drawing_area); + + if (!context) + return FALSE; + + const guint width = gtk_widget_get_allocated_width(drawing_area); + const guint height = gtk_widget_get_allocated_height(drawing_area); + + gtk_render_background(context, cairo, 0, 0, width, height); + + if ((!(handle->qr)) || (handle->qr->width <= 0)) + return FALSE; + + const guint m = 3; + const guint w = handle->qr->width; + const guint w2 = w + m * 2; + + guchar pixels [w2 * w2 * 3]; + + guint x, y, z; + for (y = 0; y < w2; y++) + for (x = 0; x < w2; x++) + { + guchar value; + + if ((x >= m) && (y >= m) && (x - m < w) && (y - m < w)) + value = ((handle->qr->data[(y - m) * w + x - m]) & 1); + else + value = 0; + + for (z = 0; z < 3; z++) + pixels[(y * w2 + x) * 3 + z] = value? 0x00 : 0xff; + } + + GdkPixbuf *image = gdk_pixbuf_new_from_data( + pixels, + GDK_COLORSPACE_RGB, + FALSE, + 8, + w2, + w2, + w2 * 3, + NULL, + NULL + ); + + if (!image) + return FALSE; + + int dwidth = gdk_pixbuf_get_width(image); + int dheight = gdk_pixbuf_get_height(image); + + double ratio_width = 1.0 * width / dwidth; + double ratio_height = 1.0 * height / dheight; + + const double ratio = ratio_width < ratio_height? ratio_width : ratio_height; + + dwidth = (int) (dwidth * ratio); + dheight = (int) (dheight * ratio); + + double dx = (width - dwidth) * 0.5; + double dy = (height - dheight) * 0.5; + + const int interp_type = (ratio >= 1.0? + GDK_INTERP_NEAREST : + GDK_INTERP_BILINEAR + ); + + GdkPixbuf* scaled = gdk_pixbuf_scale_simple( + image, + dwidth, + dheight, + interp_type + ); + + gtk_render_icon(context, cairo, scaled, dx, dy); + + cairo_fill(cairo); + + g_object_unref(scaled); + g_object_unref(image); + + return FALSE; +} + + +void +ui_new_lobby_dialog_init(MESSENGER_Application *app, + UI_NEW_LOBBY_Handle *handle) +{ + handle->builder = gtk_builder_new_from_resource( + application_get_resource_path(app, "ui/new_lobby.ui") + ); + + handle->dialog = GTK_DIALOG( + gtk_builder_get_object(handle->builder, "new_lobby_dialog") + ); + + gtk_window_set_transient_for( + GTK_WINDOW(handle->dialog), + GTK_WINDOW(app->ui.messenger.main_window) + ); + + handle->warning_info_bar = GTK_INFO_BAR( + gtk_builder_get_object(handle->builder, "warning_info_bar") + ); + + g_signal_connect( + handle->warning_info_bar, + "close", + G_CALLBACK(handle_warning_info_bar_close), + NULL + ); + + g_signal_connect( + handle->warning_info_bar, + "response", + G_CALLBACK(handle_warning_info_bar_response), + NULL + ); + + handle->stack = GTK_STACK( + gtk_builder_get_object(handle->builder, "new_lobby_stack") + ); + + handle->generate_box = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "generate_box") + ); + + handle->copy_box = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "copy_box") + ); + + handle->expiration_combo_box = GTK_COMBO_BOX( + gtk_builder_get_object(handle->builder, "expiration_combo_box") + ); + + handle->preview_stack = GTK_STACK( + gtk_builder_get_object(handle->builder, "preview_stack") + ); + + handle->fail_box = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "fail_box") + ); + + handle->loading_spinner = GTK_SPINNER( + gtk_builder_get_object(handle->builder, "loading_spinner") + ); + + handle->id_drawing_area = GTK_DRAWING_AREA( + gtk_builder_get_object(handle->builder, "id_drawing_area") + ); + + handle->id_draw_signal = g_signal_connect( + handle->id_drawing_area, + "draw", + G_CALLBACK(handle_id_drawing_area_draw), + handle + ); + + handle->id_entry = GTK_ENTRY( + gtk_builder_get_object(handle->builder, "id_entry") + ); + + handle->cancel_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "cancel_button") + ); + + g_signal_connect( + handle->cancel_button, + "clicked", + G_CALLBACK(handle_cancel_button_click), + handle->dialog + ); + + handle->generate_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "generate_button") + ); + + g_signal_connect( + handle->generate_button, + "clicked", + G_CALLBACK(handle_generate_button_click), + app + ); + + handle->copy_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "copy_button") + ); + + g_signal_connect( + handle->copy_button, + "clicked", + G_CALLBACK(handle_copy_button_click), + app + ); + + g_signal_connect( + handle->dialog, + "destroy", + G_CALLBACK(handle_dialog_destroy), + handle + ); +} + + +void +ui_new_lobby_dialog_cleanup(UI_NEW_LOBBY_Handle *handle) +{ + g_signal_handler_disconnect( + handle->id_drawing_area, + handle->id_draw_signal + ); + + g_object_unref(handle->builder); + + if (handle->qr) + QRcode_free(handle->qr); + + memset(handle, 0, sizeof(*handle)); +} 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 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file ui/new_lobby.h + */ + +#ifndef UI_NEW_LOBBY_H_ +#define UI_NEW_LOBBY_H_ + +#include "messenger.h" + +#include +#include +#include + +typedef struct UI_NEW_LOBBY_Handle +{ + GtkBuilder *builder; + GtkDialog *dialog; + + GtkInfoBar *warning_info_bar; + + GtkStack *stack; + GtkWidget *generate_box; + GtkWidget *copy_box; + + GtkComboBox *expiration_combo_box; + + GtkStack *preview_stack; + GtkWidget *fail_box; + + GtkSpinner *loading_spinner; + + GtkDrawingArea *id_drawing_area; + GtkEntry *id_entry; + + gulong id_draw_signal; + + GtkButton *cancel_button; + GtkButton *generate_button; + GtkButton *copy_button; + + QRcode *qr; +} UI_NEW_LOBBY_Handle; + +void +ui_new_lobby_dialog_init(MESSENGER_Application *app, + UI_NEW_LOBBY_Handle *handle); + +void +ui_new_lobby_dialog_cleanup(UI_NEW_LOBBY_Handle *handle); + +#endif /* UI_NEW_LOBBY_H_ */ -- cgit v1.2.3