messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

commit 53334c28a6a97e480299c6cef1e2e0402b4265ed
parent 965c55597da500e54c9cba061888ea4774fc056a
Author: TheJackiMonster <thejackimonster@gmail.com>
Date:   Fri,  3 Dec 2021 22:23:37 +0100

Added new group dialog

Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>

Diffstat:
MMakefile | 1+
Aresources/ui/new_group.ui | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mresources/ui/new_platform.ui | 5+----
Msrc/application.h | 2++
Msrc/ui/messenger.c | 20++++++++++++++++++++
Asrc/ui/new_group.c | 353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ui/new_group.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 669 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile @@ -14,6 +14,7 @@ SOURCES = messenger_gtk.c\ ui/message.c\ ui/messenger.c\ ui/new_contact.c\ + ui/new_group.c\ ui/new_platform.c\ ui/picker.c\ ui/profile_entry.c\ diff --git a/resources/ui/new_group.ui b/resources/ui/new_group.ui @@ -0,0 +1,231 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 + +Copyright (C) 2021 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 <http://www.gnu.org/licenses/>. + +SPDX-License-Identifier: AGPL3.0-or-later +Author: Tobias Frisch + +--> +<interface> + <requires lib="gtk+" version="3.24"/> + <requires lib="libhandy" version="1.2"/> + <object class="GtkDialog" id="new_group_dialog"> + <property name="can-focus">False</property> + <property name="modal">True</property> + <property name="window-position">center-on-parent</property> + <property name="type-hint">dialog</property> + <child internal-child="vbox"> + <object class="GtkBox"> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child internal-child="action_area"> + <object class="GtkButtonBox"> + <property name="can-focus">False</property> + <property name="layout-style">end</property> + <child> + <object class="GtkButton" id="cancel_button"> + <property name="label" translatable="yes">Cancel</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="previous_button"> + <property name="label" translatable="yes">Previous</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="next_button"> + <property name="label" translatable="yes">Next</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="confirm_button"> + <property name="label" translatable="yes">Confirm</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkStack" id="new_group_stack"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkBox" id="details_box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">center</property> + <property name="border-width">8</property> + <property name="orientation">vertical</property> + <property name="spacing">4</property> + <child> + <object class="HdyAvatar" id="group_avatar"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">center</property> + <property name="margin-top">8</property> + <property name="margin-bottom">8</property> + <property name="icon-name">system-users-symbolic</property> + <property name="size">128</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Group:</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="group_entry"> + <property name="width-request">250</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkFileChooserButton" id="group_avatar_file"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="title" translatable="yes"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="name">details_page</property> + </packing> + </child> + <child> + <object class="GtkBox" id="contacts_box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkSearchEntry" id="contact_search_entry"> + <property name="width-request">250</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="primary-icon-name">edit-find-symbolic</property> + <property name="primary-icon-activatable">False</property> + <property name="primary-icon-sensitive">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow"> + <property name="height-request">200</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkListBox" id="contacts_listbox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="selection-mode">multiple</property> + <property name="activate-on-single-click">False</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="pack-type">end</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="name">contacts_page</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/resources/ui/new_platform.ui b/resources/ui/new_platform.ui @@ -75,10 +75,7 @@ Author: Tobias Frisch <property name="visible">True</property> <property name="can-focus">False</property> <property name="halign">center</property> - <property name="margin-start">8</property> - <property name="margin-end">8</property> - <property name="margin-top">8</property> - <property name="margin-bottom">8</property> + <property name="border-width">8</property> <property name="orientation">vertical</property> <property name="spacing">4</property> <child> diff --git a/src/application.h b/src/application.h @@ -33,6 +33,7 @@ #include "ui/contacts.h" #include "ui/messenger.h" #include "ui/new_contact.h" +#include "ui/new_group.h" #include "ui/new_platform.h" #include "ui/settings.h" @@ -71,6 +72,7 @@ typedef struct MESSENGER_Application UI_MESSENGER_Handle messenger; UI_NEW_CONTACT_Handle new_contact; + UI_NEW_GROUP_Handle new_group; UI_NEW_PLATFORM_Handle new_platform; UI_CONTACTS_Handle contacts; UI_SETTINGS_Handle settings; diff --git a/src/ui/messenger.c b/src/ui/messenger.c @@ -83,6 +83,19 @@ handle_new_contact_button_click(UNUSED GtkButton* button, } static void +handle_new_group_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_group_dialog_init(app, &(app->ui.new_group)); + + gtk_widget_show(GTK_WIDGET(app->ui.new_group.dialog)); +} + +static void handle_new_platform_button_click(UNUSED GtkButton* button, gpointer user_data) { @@ -281,6 +294,13 @@ ui_messenger_init(MESSENGER_Application *app, ); g_signal_connect( + handle->new_group_button, + "clicked", + G_CALLBACK(handle_new_group_button_click), + app + ); + + g_signal_connect( handle->new_platform_button, "clicked", G_CALLBACK(handle_new_platform_button_click), diff --git a/src/ui/new_group.c b/src/ui/new_group.c @@ -0,0 +1,353 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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 <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file ui/new_group.c + */ + +#include "new_platform.h" + +#include "contact_entry.h" +#include "../application.h" + +static void +_open_new_group(GtkEntry *entry, + GtkListBox *listbox, + MESSENGER_Application *app) +{ + const gchar *name = gtk_entry_get_text(entry); + + struct GNUNET_CHAT_Group *group = GNUNET_CHAT_group_create( + app->chat.messenger.handle, + NULL + ); + + if ((name) && (strlen(name) > 0)) + GNUNET_CHAT_group_set_name(group, name); + + GList *selected = gtk_list_box_get_selected_rows(listbox); + + while (selected) + { + if (selected->data) + { + GtkListBoxRow *row = GTK_LIST_BOX_ROW(selected->data); + + struct GNUNET_CHAT_Contact* contact = g_hash_table_lookup( + app->ui.bindings, row + ); + + GNUNET_CHAT_group_invite_contact(group, contact); + } + + selected = selected->next; + } +} + +static void +handle_group_entry_changed(GtkEditable *editable, + gpointer user_data) +{ + HdyAvatar *avatar = HDY_AVATAR(user_data); + GtkEntry *entry = GTK_ENTRY(editable); + + hdy_avatar_set_text(avatar, gtk_entry_get_text(entry)); +} + +static void +_go_page_details(UI_NEW_GROUP_Handle *handle) +{ + gtk_stack_set_visible_child(handle->stack, handle->details_box); + + gtk_widget_hide(GTK_WIDGET(handle->previous_button)); + gtk_widget_hide(GTK_WIDGET(handle->confirm_button)); + + gtk_widget_show(GTK_WIDGET(handle->cancel_button)); + gtk_widget_show(GTK_WIDGET(handle->next_button)); +} + +static void +_go_page_contacts(UI_NEW_GROUP_Handle *handle) +{ + gtk_stack_set_visible_child(handle->stack, handle->contacts_box); + + gtk_widget_hide(GTK_WIDGET(handle->cancel_button)); + gtk_widget_hide(GTK_WIDGET(handle->next_button)); + + gtk_widget_show(GTK_WIDGET(handle->previous_button)); + gtk_widget_show(GTK_WIDGET(handle->confirm_button)); +} + +static void +handle_group_entry_activate(UNUSED GtkEntry *entry, + gpointer user_data) +{ + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + _go_page_contacts(&(app->ui.new_group)); +} + +static void +handle_cancel_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + GtkDialog *dialog = GTK_DIALOG(user_data); + gtk_window_close(GTK_WINDOW(dialog)); +} + +static void +handle_previous_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + _go_page_details((UI_NEW_GROUP_Handle*) user_data); +} + +static void +handle_next_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + _go_page_contacts((UI_NEW_GROUP_Handle*) user_data); +} + +static void +handle_confirm_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + _open_new_group( + app->ui.new_group.group_entry, + app->ui.new_group.contacts_listbox, + app + ); + + gtk_window_close(GTK_WINDOW(app->ui.new_group.dialog)); +} + +static void +handle_dialog_destroy(UNUSED GtkWidget *window, + gpointer user_data) +{ + ui_new_group_dialog_cleanup((UI_NEW_GROUP_Handle*) user_data); +} + +static int +_iterate_clear_contacts(UNUSED void *cls, + UNUSED struct GNUNET_CHAT_Handle *handle, + struct GNUNET_CHAT_Contact *contact) +{ + GNUNET_CHAT_contact_set_user_pointer(contact, NULL); + return GNUNET_YES; +} + +static int +_iterate_contacts(void *cls, + UNUSED struct GNUNET_CHAT_Handle *handle, + struct GNUNET_CHAT_Contact *contact) +{ + MESSENGER_Application *app = (MESSENGER_Application*) cls; + + if (GNUNET_CHAT_contact_get_user_pointer(contact)) + return GNUNET_YES; + + const char *title; + title = GNUNET_CHAT_contact_get_name(contact); + + const char *key = GNUNET_CHAT_contact_get_key(contact); + + UI_CONTACT_ENTRY_Handle *entry = ui_contact_entry_new(); + gtk_list_box_prepend( + app->ui.new_group.contacts_listbox, + entry->entry_box + ); + + GNUNET_CHAT_contact_set_user_pointer(contact, entry); + + if (title) + { + gtk_label_set_text(entry->title_label, title); + hdy_avatar_set_text(entry->entry_avatar, title); + } + + if (key) + gtk_label_set_text(entry->subtitle_label, key); + + GtkListBoxRow *row = GTK_LIST_BOX_ROW( + gtk_widget_get_parent(entry->entry_box) + ); + + g_hash_table_insert(app->ui.bindings, row, contact); + + app->ui.new_group.contact_entries = g_list_append( + app->ui.new_group.contact_entries, + entry + ); + + return GNUNET_YES; +} + +void +ui_new_group_dialog_init(MESSENGER_Application *app, + UI_NEW_GROUP_Handle *handle) +{ + handle->contact_entries = g_list_alloc(); + + handle->builder = gtk_builder_new_from_file("resources/ui/new_group.ui"); + + handle->dialog = GTK_DIALOG( + gtk_builder_get_object(handle->builder, "new_group_dialog") + ); + + gtk_window_set_title( + GTK_WINDOW(handle->dialog), + "New Group" + ); + + gtk_window_set_transient_for( + GTK_WINDOW(handle->dialog), + GTK_WINDOW(app->ui.messenger.main_window) + ); + + handle->stack = GTK_STACK( + gtk_builder_get_object(handle->builder, "new_group_stack") + ); + + handle->details_box = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "details_box") + ); + + handle->contacts_box = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "contacts_box") + ); + + handle->group_avatar = HDY_AVATAR( + gtk_builder_get_object(handle->builder, "group_avatar") + ); + + handle->group_avatar_file = GTK_FILE_CHOOSER_BUTTON( + gtk_builder_get_object(handle->builder, "group_avatar_file") + ); + + handle->group_entry = GTK_ENTRY( + gtk_builder_get_object(handle->builder, "group_entry") + ); + + g_signal_connect( + handle->group_entry, + "changed", + G_CALLBACK(handle_group_entry_changed), + handle->group_avatar + ); + + g_signal_connect( + handle->group_entry, + "activate", + G_CALLBACK(handle_group_entry_activate), + app + ); + + handle->contact_search_entry = GTK_SEARCH_ENTRY( + gtk_builder_get_object(handle->builder, "contact_search_entry") + ); + + handle->contacts_listbox = GTK_LIST_BOX( + gtk_builder_get_object(handle->builder, "contacts_listbox") + ); + + 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->previous_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "previous_button") + ); + + g_signal_connect( + handle->previous_button, + "clicked", + G_CALLBACK(handle_previous_button_click), + handle + ); + + handle->next_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "next_button") + ); + + g_signal_connect( + handle->next_button, + "clicked", + G_CALLBACK(handle_next_button_click), + handle + ); + + handle->confirm_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "confirm_button") + ); + + g_signal_connect( + handle->confirm_button, + "clicked", + G_CALLBACK(handle_confirm_button_click), + app + ); + + g_signal_connect( + handle->dialog, + "destroy", + G_CALLBACK(handle_dialog_destroy), + handle + ); + + GNUNET_CHAT_iterate_contacts( + app->chat.messenger.handle, + _iterate_clear_contacts, + NULL + ); + + GNUNET_CHAT_iterate_contacts( + app->chat.messenger.handle, + _iterate_contacts, + app + ); +} + +void +ui_new_group_dialog_cleanup(UI_NEW_GROUP_Handle *handle) +{ + g_object_unref(handle->builder); + + GList *list = handle->contact_entries; + + while (list) { + if (list->data) + ui_contact_entry_delete((UI_CONTACT_ENTRY_Handle*) list->data); + + list = list->next; + } + + g_list_free(handle->contact_entries); +} diff --git a/src/ui/new_group.h b/src/ui/new_group.h @@ -0,0 +1,61 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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 <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file ui/new_group.h + */ + +#ifndef UI_NEW_GROUP_H_ +#define UI_NEW_GROUP_H_ + +typedef struct UI_NEW_GROUP_Handle +{ + GList *contact_entries; + + GtkBuilder *builder; + GtkDialog *dialog; + + GtkStack *stack; + GtkWidget *details_box; + GtkWidget *contacts_box; + + HdyAvatar *group_avatar; + GtkFileChooserButton *group_avatar_file; + + GtkEntry *group_entry; + + GtkSearchEntry *contact_search_entry; + + GtkListBox *contacts_listbox; + + GtkButton *cancel_button; + GtkButton *previous_button; + GtkButton *next_button; + GtkButton *confirm_button; +} UI_NEW_GROUP_Handle; + +void +ui_new_group_dialog_init(MESSENGER_Application *app, + UI_NEW_GROUP_Handle *handle); + +void +ui_new_group_dialog_cleanup(UI_NEW_GROUP_Handle *handle); + +#endif /* UI_NEW_GROUP_H_ */