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:
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_ */