commit 56a206cd17e1caf23fbfd679c39752e5e194f35d
parent 83bf19ca1bc25c2162fa7e1d9f06644dd7e6b267
Author: TheJackiMonster <thejackimonster@gmail.com>
Date: Sun, 14 Nov 2021 17:31:52 +0100
Implemented multiple chats in parallel
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
Diffstat:
11 files changed, 672 insertions(+), 503 deletions(-)
diff --git a/Makefile b/Makefile
@@ -7,6 +7,7 @@ SOURCES = messenger_gtk.c\
application.c\
event.c\
chat/messenger.c\
+ ui/chat.c\
ui/chat_entry.c\
ui/message.c\
ui/messenger.c\
diff --git a/resources/ui/chat.ui b/resources/ui/chat.ui
@@ -0,0 +1,268 @@
+<?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"/>
+ <object class="GtkBox" id="chat_box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="chat-header-box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</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="spacing">8</property>
+ <child>
+ <object class="GtkButton" id="back_button">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">go-previous-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin-start">4</property>
+ <property name="margin-end">4</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="chat_title">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Chat title</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="chat_subtitle">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Chat subtitle</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="light"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="chat_details_button">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">view-more-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </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="visible">True</property>
+ <property name="can-focus">True</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkStack">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkListBox" id="messages_listbox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="selection-mode">none</property>
+ <property name="activate-on-single-click">False</property>
+ </object>
+ <packing>
+ <property name="name">page0</property>
+ <property name="title" translatable="yes">page0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">8</property>
+ <property name="margin-end">8</property>
+ <property name="margin-top">4</property>
+ <property name="margin-bottom">4</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkButton" id="attach_file_button">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="valign">center</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">mail-attachment-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTextView" id="send_text_view">
+ <property name="width-request">210</property>
+ <property name="height-request">48</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="valign">end</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="left-margin">8</property>
+ <property name="right-margin">8</property>
+ <property name="top-margin">8</property>
+ <property name="bottom-margin">8</property>
+ <property name="input-hints">GTK_INPUT_HINT_SPELLCHECK | GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_INHIBIT_OSK | GTK_INPUT_HINT_EMOJI | GTK_INPUT_HINT_NONE</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="emoji_button">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="valign">center</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">face-smile-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="send_record_button">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="valign">center</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="send_record_symbol">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">audio-input-microphone-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/resources/ui/messenger.ui b/resources/ui/messenger.ui
@@ -717,248 +717,12 @@ Author: Tobias Frisch
<property name="flap-position">end</property>
<property name="reveal-flap">False</property>
<property name="fold-policy">always</property>
- <child type="content">
- <object class="GtkBox">
+ <child>
+ <object class="GtkStack" id="chats_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="orientation">vertical</property>
<child>
- <object class="GtkBox" id="chat-header-box">
- <property name="visible">True</property>
- <property name="can-focus">False</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="spacing">8</property>
- <child>
- <object class="GtkButton" id="back_button">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">True</property>
- <property name="relief">none</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="icon-name">go-previous-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="halign">start</property>
- <property name="margin-start">4</property>
- <property name="margin-end">4</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkLabel" id="chat_title">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Chat title</property>
- <property name="xalign">0</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="chat_subtitle">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Chat subtitle</property>
- <property name="xalign">0</property>
- <attributes>
- <attribute name="weight" value="light"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="chat_details_button">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">True</property>
- <property name="relief">none</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="icon-name">view-more-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack-type">end</property>
- <property name="position">1</property>
- </packing>
- </child>
- </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="visible">True</property>
- <property name="can-focus">True</property>
- <child>
- <object class="GtkViewport">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <child>
- <object class="GtkStack">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <child>
- <object class="GtkListBox" id="messages_listbox">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="selection-mode">none</property>
- <property name="activate-on-single-click">False</property>
- </object>
- <packing>
- <property name="name">page0</property>
- <property name="title" translatable="yes">page0</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="margin-start">8</property>
- <property name="margin-end">8</property>
- <property name="margin-top">4</property>
- <property name="margin-bottom">4</property>
- <property name="spacing">4</property>
- <child>
- <object class="GtkButton" id="attach_file_button">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">True</property>
- <property name="valign">center</property>
- <property name="relief">none</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="icon-name">mail-attachment-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkTextView" id="send_text_view">
- <property name="width-request">210</property>
- <property name="height-request">48</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="valign">end</property>
- <property name="wrap-mode">word-char</property>
- <property name="left-margin">8</property>
- <property name="right-margin">8</property>
- <property name="top-margin">8</property>
- <property name="bottom-margin">8</property>
- <property name="input-hints">GTK_INPUT_HINT_SPELLCHECK | GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_INHIBIT_OSK | GTK_INPUT_HINT_EMOJI | GTK_INPUT_HINT_NONE</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="emoji_button">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">True</property>
- <property name="valign">center</property>
- <property name="relief">none</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="icon-name">face-smile-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="send_record_button">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">True</property>
- <property name="valign">center</property>
- <property name="relief">none</property>
- <child>
- <object class="GtkImage" id="send_record_symbol">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="icon-name">audio-input-microphone-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
+ <placeholder/>
</child>
</object>
</child>
@@ -1108,12 +872,4 @@ Author: Tobias Frisch
<widget name="settings-label"/>
</widgets>
</object>
- <object class="GtkSizeGroup">
- <property name="mode">vertical</property>
- <widgets>
- <widget name="chats-header-box"/>
- <widget name="chat-header-box"/>
- <widget name="details-header-box"/>
- </widgets>
- </object>
</interface>
diff --git a/src/application.c b/src/application.c
@@ -48,7 +48,7 @@ _application_activate(UNUSED GtkApplication* application,
{
MESSENGER_Application *app = (MESSENGER_Application*) user_data;
- ui_messenger_run(app);
+ ui_messenger_init(app, &(app->ui.messenger));
}
void
@@ -140,6 +140,8 @@ application_run(MESSENGER_Application *app)
pthread_join(app->chat.tid, NULL);
+ ui_messenger_cleanup(&(app->ui.messenger));
+
g_hash_table_destroy(app->ui.bindings);
notify_uninit();
diff --git a/src/event.c b/src/event.c
@@ -27,21 +27,43 @@
#include "ui/chat_entry.h"
#include "ui/message.h"
-static int
-_iterate_profile_contacts(void *cls,
- UNUSED struct GNUNET_CHAT_Handle *handle,
- UNUSED struct GNUNET_CHAT_Contact *contact)
+static void
+_add_new_chat_entry(MESSENGER_Application *app,
+ struct GNUNET_CHAT_Context *context)
{
- MESSENGER_Application *app = (MESSENGER_Application*) cls;
-
UI_MESSENGER_Handle *ui = &(app->ui.messenger);
- UI_CHAT_ENTRY_Handle *entry = ui_chat_entry_new();
-
+ UI_CHAT_ENTRY_Handle *entry = ui_chat_entry_new(app);
gtk_container_add(GTK_CONTAINER(ui->chats_listbox), entry->entry_box);
+ GNUNET_CHAT_context_set_user_pointer(context, entry);
+
+ char context_id [9];
+ g_snprintf(context_id, sizeof(context_id), "%08lx", (gulong) context);
+
+ gtk_widget_set_name(entry->entry_box, context_id);
- g_free(entry); //TODO: add to a list or similar?
+ gtk_stack_add_named(
+ ui->chats_stack,
+ entry->chat->chat_box,
+ context_id
+ );
+ g_hash_table_insert(
+ app->ui.bindings,
+ entry->chat->send_text_view,
+ context
+ );
+
+ ui->chat_entries = g_list_append(ui->chat_entries, entry);
+}
+
+static int
+_iterate_profile_contacts(void *cls,
+ UNUSED struct GNUNET_CHAT_Handle *handle,
+ UNUSED struct GNUNET_CHAT_Contact *contact)
+{
+ MESSENGER_Application *app = (MESSENGER_Application*) cls;
+ _add_new_chat_entry(app, GNUNET_CHAT_contact_get_context(contact));
return GNUNET_YES;
}
@@ -51,15 +73,7 @@ _iterate_profile_groups(void *cls,
UNUSED struct GNUNET_CHAT_Group *group)
{
MESSENGER_Application *app = (MESSENGER_Application*) cls;
-
- UI_MESSENGER_Handle *ui = &(app->ui.messenger);
-
- UI_CHAT_ENTRY_Handle *entry = ui_chat_entry_new();
-
- gtk_container_add(GTK_CONTAINER(ui->chats_listbox), entry->entry_box);
-
- g_free(entry); //TODO: add to a list or similar?
-
+ _add_new_chat_entry(app, GNUNET_CHAT_group_get_context(group));
return GNUNET_YES;
}
@@ -96,12 +110,6 @@ event_update_profile(MESSENGER_Application *app,
GNUNET_CHAT_iterate_contacts(chat->handle, _iterate_profile_contacts, app);
GNUNET_CHAT_iterate_groups(chat->handle, _iterate_profile_groups, app);
-
- for (int i = 0; i < 8; i++) {
- UI_MESSAGE_Handle *message = ui_message_new(app, i % 2 == 0);
- gtk_container_add(GTK_CONTAINER(ui->messages_listbox), message->message_box);
- g_free(message); // TODO: this is just a test!
- }
}
void
@@ -117,26 +125,7 @@ event_update_chats(MESSENGER_Application *app,
if (GNUNET_CHAT_context_get_user_pointer(context))
return;
- UI_MESSENGER_Handle *ui = &(app->ui.messenger);
-
- UI_CHAT_ENTRY_Handle *entry = ui_chat_entry_new();
- gtk_container_add(GTK_CONTAINER(ui->chats_listbox), entry->entry_box);
- g_free(entry); // TODO: free already?
-
- // TODO: put something better here to attach it!
- GNUNET_CHAT_context_set_user_pointer(context, ui);
-
- g_hash_table_insert(
- app->ui.bindings,
- app->ui.messenger.send_record_button,
- context
- );
-
- g_hash_table_insert(
- app->ui.bindings,
- app->ui.messenger.send_text_view,
- context
- );
+ _add_new_chat_entry(app, context);
}
void
@@ -149,7 +138,9 @@ event_receive_message(MESSENGER_Application *app,
struct GNUNET_CHAT_Context *context = (struct GNUNET_CHAT_Context*) argv[0];
- if (!GNUNET_CHAT_context_get_user_pointer(context))
+ UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context);
+
+ if (!handle)
return;
const struct GNUNET_CHAT_Message *msg;
@@ -185,9 +176,12 @@ event_receive_message(MESSENGER_Application *app,
// TODO: check read receipt
gtk_container_add(
- GTK_CONTAINER(app->ui.messenger.messages_listbox),
+ GTK_CONTAINER(handle->chat->messages_listbox),
message->message_box
);
g_free(message); // TODO: this is just a test!
+
+ gtk_label_set_text(handle->text_label, text? text : "");
+ gtk_label_set_text(handle->timestamp_label, time? time : "");
}
diff --git a/src/ui/chat.c b/src/ui/chat.c
@@ -0,0 +1,249 @@
+/*
+ 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/chat.c
+ */
+
+#include "chat.h"
+
+#include "messenger.h"
+#include "../application.h"
+
+static void
+handle_flap_via_button_click(UNUSED GtkButton* button,
+ gpointer user_data)
+{
+ HdyFlap* flap = HDY_FLAP(user_data);
+
+ if (TRUE == hdy_flap_get_reveal_flap(flap)) {
+ hdy_flap_set_reveal_flap(flap, FALSE);
+ } else {
+ hdy_flap_set_reveal_flap(flap, TRUE);
+ }
+}
+
+static void
+handle_back_button_click(UNUSED GtkButton* button,
+ gpointer user_data)
+{
+ HdyLeaflet* leaflet = HDY_LEAFLET(user_data);
+
+ GList* children = gtk_container_get_children(GTK_CONTAINER(leaflet));
+
+ if (children) {
+ hdy_leaflet_set_visible_child(leaflet, GTK_WIDGET(children->data));
+ }
+}
+
+static void
+handle_send_text_buffer_changed(GtkTextBuffer *buffer,
+ gpointer user_data)
+{
+ GtkImage *symbol = GTK_IMAGE(user_data);
+
+ GtkTextIter start, end;
+ gtk_text_buffer_get_start_iter(buffer, &start);
+ gtk_text_buffer_get_end_iter(buffer, &end);
+
+ const gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
+
+ gtk_image_set_from_icon_name(
+ symbol,
+ 0 < g_utf8_strlen(text, 1)?
+ "mail-send-symbolic" :
+ "audio-input-microphone-symbolic",
+ GTK_ICON_SIZE_BUTTON
+ );
+}
+
+static gboolean
+_send_text_from_view(MESSENGER_Application *app,
+ GtkTextView *text_view)
+{
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view);
+
+ GtkTextIter start, end;
+ gtk_text_buffer_get_start_iter(buffer, &start);
+ gtk_text_buffer_get_end_iter(buffer, &end);
+
+ const gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
+
+ if (0 == g_utf8_strlen(text, 1))
+ return FALSE;
+
+ struct GNUNET_CHAT_Context *context = g_hash_table_lookup(
+ app->ui.bindings, text_view
+ );
+
+ if (context)
+ GNUNET_CHAT_context_send_text(context, text);
+
+ gtk_text_buffer_delete(buffer, &start, &end);
+ return TRUE;
+}
+
+static void
+handle_send_record_button_click(GtkButton *button,
+ gpointer user_data)
+{
+ MESSENGER_Application *app = (MESSENGER_Application*) user_data;
+
+ GtkTextView *text_view = GTK_TEXT_VIEW(
+ g_hash_table_lookup(app->ui.bindings, button)
+ );
+
+ if (!_send_text_from_view(app, text_view))
+ {
+ // TODO: record audio and attach as file?
+ }
+}
+
+static gboolean
+handle_send_text_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ MESSENGER_Application *app = (MESSENGER_Application*) user_data;
+
+ if ((app->ui.mobile) ||
+ (event->state & GDK_SHIFT_MASK) ||
+ ((event->keyval != GDK_KEY_Return) &&
+ (event->keyval != GDK_KEY_KP_Enter)))
+ return FALSE;
+
+ return _send_text_from_view(app, GTK_TEXT_VIEW(widget));
+}
+
+UI_CHAT_Handle*
+ui_chat_new(MESSENGER_Application *app)
+{
+ UI_CHAT_Handle *handle = g_malloc(sizeof(UI_CHAT_Handle));
+ UI_MESSENGER_Handle *messenger = &(app->ui.messenger);
+
+ GtkBuilder* builder = gtk_builder_new_from_file(
+ "resources/ui/chat.ui"
+ );
+
+ handle->chat_box = GTK_WIDGET(
+ gtk_builder_get_object(builder, "chat_box")
+ );
+
+ handle->back_button = GTK_BUTTON(
+ gtk_builder_get_object(builder, "back_button")
+ );
+
+ g_object_bind_property(
+ messenger->leaflet_chat,
+ "folded",
+ handle->back_button,
+ "visible",
+ G_BINDING_SYNC_CREATE
+ );
+
+ g_signal_connect(
+ handle->back_button,
+ "clicked",
+ G_CALLBACK(handle_back_button_click),
+ messenger->leaflet_chat
+ );
+
+ handle->chat_title = GTK_LABEL(
+ gtk_builder_get_object(builder, "chat_title")
+ );
+
+ handle->chat_subtitle = GTK_LABEL(
+ gtk_builder_get_object(builder, "chat_subtitle")
+ );
+
+ handle->chat_details_button = GTK_BUTTON(
+ gtk_builder_get_object(builder, "chat_details_button")
+ );
+
+ g_signal_connect(
+ handle->chat_details_button,
+ "clicked",
+ G_CALLBACK(handle_flap_via_button_click),
+ messenger->flap_chat_details
+ );
+
+ handle->messages_listbox = GTK_LIST_BOX(
+ gtk_builder_get_object(builder, "messages_listbox")
+ );
+
+ handle->attach_file_button = GTK_BUTTON(
+ gtk_builder_get_object(builder, "attach_file_button")
+ );
+
+ handle->send_text_view = GTK_TEXT_VIEW(
+ gtk_builder_get_object(builder, "send_text_view")
+ );
+
+ handle->emoji_button = GTK_BUTTON(
+ gtk_builder_get_object(builder, "emoji_button")
+ );
+
+ handle->send_record_button = GTK_BUTTON(
+ gtk_builder_get_object(builder, "send_record_button")
+ );
+
+ handle->send_record_symbol = GTK_IMAGE(
+ gtk_builder_get_object(builder, "send_record_symbol")
+ );
+
+ GtkTextBuffer *send_text_buffer = gtk_text_view_get_buffer(
+ handle->send_text_view
+ );
+
+ g_signal_connect(
+ send_text_buffer,
+ "changed",
+ G_CALLBACK(handle_send_text_buffer_changed),
+ handle->send_record_symbol
+ );
+
+ g_signal_connect(
+ handle->send_record_button,
+ "clicked",
+ G_CALLBACK(handle_send_record_button_click),
+ app
+ );
+
+ g_signal_connect(
+ handle->send_text_view,
+ "key-press-event",
+ G_CALLBACK(handle_send_text_key_press),
+ app
+ );
+
+ g_hash_table_insert(
+ app->ui.bindings,
+ handle->send_record_button,
+ handle->send_text_view
+ );
+
+ return handle;
+}
+
+void
+ui_chat_delete(UI_CHAT_Handle *handle)
+{
+ g_free(handle);
+}
diff --git a/src/ui/chat.h b/src/ui/chat.h
@@ -0,0 +1,59 @@
+/*
+ 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/chat.h
+ */
+
+#ifndef UI_CHAT_H_
+#define UI_CHAT_H_
+
+#include <gtk-3.0/gtk/gtk.h>
+#include <libhandy-1/handy.h>
+#include <libnotify/notify.h>
+
+typedef struct MESSENGER_Application MESSENGER_Application;
+
+typedef struct UI_CHAT_Handle
+{
+ GtkWidget *chat_box;
+
+ GtkButton *back_button;
+
+ GtkLabel *chat_title;
+ GtkLabel *chat_subtitle;
+ GtkButton *chat_details_button;
+
+ GtkListBox *messages_listbox;
+
+ GtkButton *attach_file_button;
+ GtkTextView *send_text_view;
+ GtkButton *emoji_button;
+ GtkButton *send_record_button;
+ GtkImage *send_record_symbol;
+} UI_CHAT_Handle;
+
+UI_CHAT_Handle*
+ui_chat_new(MESSENGER_Application *app);
+
+void
+ui_chat_delete(UI_CHAT_Handle *handle);
+
+#endif /* UI_CHAT_H_ */
diff --git a/src/ui/chat_entry.c b/src/ui/chat_entry.c
@@ -24,11 +24,15 @@
#include "chat_entry.h"
+#include "../application.h"
+
UI_CHAT_ENTRY_Handle*
-ui_chat_entry_new(void)
+ui_chat_entry_new(MESSENGER_Application *app)
{
UI_CHAT_ENTRY_Handle* handle = g_malloc(sizeof(UI_CHAT_ENTRY_Handle));
+ handle->chat = ui_chat_new(app);
+
GtkBuilder* builder = gtk_builder_new_from_file("resources/ui/chat_entry.ui");
handle->entry_box = GTK_WIDGET(
@@ -57,3 +61,11 @@ ui_chat_entry_new(void)
return handle;
}
+
+void
+ui_chat_entry_delete(UI_CHAT_ENTRY_Handle *handle)
+{
+ ui_chat_delete(handle->chat);
+
+ g_free(handle);
+}
diff --git a/src/ui/chat_entry.h b/src/ui/chat_entry.h
@@ -25,13 +25,12 @@
#ifndef UI_CHAT_ENTRY_H_
#define UI_CHAT_ENTRY_H_
-#include <gtk-3.0/gtk/gtk.h>
-#include <libhandy-1/handy.h>
-
-typedef struct MESSENGER_Application MESSENGER_Application;
+#include "chat.h"
typedef struct UI_CHAT_ENTRY_Handle
{
+ UI_CHAT_Handle *chat;
+
GtkWidget* entry_box;
HdyAvatar* entry_avatar;
@@ -44,6 +43,9 @@ typedef struct UI_CHAT_ENTRY_Handle
} UI_CHAT_ENTRY_Handle;
UI_CHAT_ENTRY_Handle*
-ui_chat_entry_new(void);
+ui_chat_entry_new(MESSENGER_Application *app);
+
+void
+ui_chat_entry_delete(UI_CHAT_ENTRY_Handle *handle);
#endif /* UI_CHAT_ENTRY_H_ */
diff --git a/src/ui/messenger.c b/src/ui/messenger.c
@@ -26,6 +26,7 @@
#include <gtk-3.0/gdk/gdkkeys.h>
+#include "chat_entry.h"
#include "message.h"
#include "new_platform.h"
#include "../application.h"
@@ -80,118 +81,25 @@ handle_new_platform_button_click(UNUSED GtkButton* button,
static void
handle_chats_listbox_row_activated(UNUSED GtkListBox* listbox,
- UNUSED GtkListBoxRow* row,
+ GtkListBoxRow* row,
gpointer user_data)
{
- HdyLeaflet* leaflet = HDY_LEAFLET(user_data);
+ UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) user_data;
+
+ GtkStack *stack = handle->chats_stack;
+ HdyLeaflet *leaflet = handle->leaflet_chat;
- GList* children = gtk_container_get_children(GTK_CONTAINER(leaflet));
+ GList *children = gtk_container_get_children(GTK_CONTAINER(leaflet));
if ((children) && (children->next)) {
hdy_leaflet_set_visible_child(leaflet, GTK_WIDGET(children->next->data));
}
-}
-
-static void
-handle_back_button_click(UNUSED GtkButton* button,
- gpointer user_data)
-{
- HdyLeaflet* leaflet = HDY_LEAFLET(user_data);
-
- GList* children = gtk_container_get_children(GTK_CONTAINER(leaflet));
-
- if (children) {
- hdy_leaflet_set_visible_child(leaflet, GTK_WIDGET(children->data));
- }
-}
-
-static void
-handle_send_text_buffer_changed(GtkTextBuffer *buffer,
- gpointer user_data)
-{
- GtkImage *symbol = GTK_IMAGE(user_data);
-
- GtkTextIter start, end;
- gtk_text_buffer_get_start_iter(buffer, &start);
- gtk_text_buffer_get_end_iter(buffer, &end);
-
- const gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
-
- gtk_image_set_from_icon_name(
- symbol,
- 0 < g_utf8_strlen(text, 1)?
- "mail-send-symbolic" :
- "audio-input-microphone-symbolic",
- GTK_ICON_SIZE_BUTTON
- );
-}
-
-static void
-handle_send_record_button_click(GtkButton *button,
- gpointer user_data)
-{
- MESSENGER_Application *app = (MESSENGER_Application*) user_data;
-
- GtkTextBuffer *buffer = gtk_text_view_get_buffer(
- app->ui.messenger.send_text_view
- );
-
- GtkTextIter start, end;
- gtk_text_buffer_get_start_iter(buffer, &start);
- gtk_text_buffer_get_end_iter(buffer, &end);
-
- const gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
-
- if (0 < g_utf8_strlen(text, 1))
- {
- struct GNUNET_CHAT_Context *context = g_hash_table_lookup(
- app->ui.bindings, button
- );
-
- if (context)
- GNUNET_CHAT_context_send_text(context, text);
- }
- else
- {
- // TODO: record audio and attach as file?
- }
-
- gtk_text_buffer_delete(buffer, &start, &end);
-}
-static gboolean
-handle_send_text_key_press (GtkWidget *widget,
- GdkEventKey *event,
- gpointer user_data)
-{
- MESSENGER_Application *app = (MESSENGER_Application*) user_data;
-
- if ((app->ui.mobile) ||
- (event->state & GDK_SHIFT_MASK) ||
- ((event->keyval != GDK_KEY_Return) &&
- (event->keyval != GDK_KEY_KP_Enter)))
- return FALSE;
-
- GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (widget));
-
- GtkTextIter start, end;
- gtk_text_buffer_get_start_iter(buffer, &start);
- gtk_text_buffer_get_end_iter(buffer, &end);
-
- const gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
-
- if (0 == g_utf8_strlen(text, 1))
- return FALSE;
-
- struct GNUNET_CHAT_Context *context = g_hash_table_lookup(
- app->ui.bindings, widget
+ GtkWidget *entry = GTK_WIDGET(
+ gtk_container_get_children(GTK_CONTAINER(row))->data
);
- if (context)
- GNUNET_CHAT_context_send_text(context, text);
-
- gtk_text_buffer_delete(buffer, &start, &end);
- return TRUE;
+ gtk_stack_set_visible_child_name(stack, gtk_widget_get_name(entry));
}
static void
@@ -207,6 +115,8 @@ void
ui_messenger_init(MESSENGER_Application *app,
UI_MESSENGER_Handle *handle)
{
+ handle->chat_entries = g_list_alloc();
+
GtkBuilder* builder = gtk_builder_new_from_file("resources/ui/messenger.ui");
handle->main_window = GTK_APPLICATION_WINDOW(
@@ -257,25 +167,6 @@ ui_messenger_init(MESSENGER_Application *app,
G_BINDING_INVERT_BOOLEAN
);
- handle->back_button = GTK_BUTTON(
- gtk_builder_get_object(builder, "back_button")
- );
-
- g_object_bind_property(
- handle->leaflet_chat,
- "folded",
- handle->back_button,
- "visible",
- G_BINDING_SYNC_CREATE
- );
-
- g_signal_connect(
- handle->back_button,
- "clicked",
- G_CALLBACK(handle_back_button_click),
- handle->leaflet_chat
- );
-
handle->profile_avatar = HDY_AVATAR(
gtk_builder_get_object(builder, "profile_avatar")
);
@@ -372,26 +263,11 @@ ui_messenger_init(MESSENGER_Application *app,
handle->chats_listbox,
"row-activated",
G_CALLBACK(handle_chats_listbox_row_activated),
- handle->leaflet_chat
- );
-
- handle->chat_title = GTK_LABEL(
- gtk_builder_get_object(builder, "chat_title")
- );
-
- handle->chat_subtitle = GTK_LABEL(
- gtk_builder_get_object(builder, "chat_subtitle")
- );
-
- handle->chat_details_button = GTK_BUTTON(
- gtk_builder_get_object(builder, "chat_details_button")
+ handle
);
- g_signal_connect(
- handle->chat_details_button,
- "clicked",
- G_CALLBACK(handle_flap_via_button_click),
- handle->flap_chat_details
+ handle->chats_stack = GTK_STACK(
+ gtk_builder_get_object(builder, "chats_stack")
);
handle->hide_chat_details_button = GTK_BUTTON(
@@ -405,55 +281,6 @@ ui_messenger_init(MESSENGER_Application *app,
handle->flap_chat_details
);
- handle->messages_listbox = GTK_LIST_BOX(
- gtk_builder_get_object(builder, "messages_listbox")
- );
-
- handle->attach_file_button = GTK_BUTTON(
- gtk_builder_get_object(builder, "attach_file_button")
- );
-
- handle->send_text_view = GTK_TEXT_VIEW(
- gtk_builder_get_object(builder, "send_text_view")
- );
-
- handle->emoji_button = GTK_BUTTON(
- gtk_builder_get_object(builder, "emoji_button")
- );
-
- handle->send_record_button = GTK_BUTTON(
- gtk_builder_get_object(builder, "send_record_button")
- );
-
- handle->send_record_symbol = GTK_IMAGE(
- gtk_builder_get_object(builder, "send_record_symbol")
- );
-
- GtkTextBuffer *send_text_buffer = gtk_text_view_get_buffer(
- handle->send_text_view
- );
-
- g_signal_connect(
- send_text_buffer,
- "changed",
- G_CALLBACK(handle_send_text_buffer_changed),
- handle->send_record_symbol
- );
-
- g_signal_connect(
- handle->send_record_button,
- "clicked",
- G_CALLBACK(handle_send_record_button_click),
- app
- );
-
- g_signal_connect(
- handle->send_text_view,
- "key-press-event",
- G_CALLBACK(handle_send_text_key_press),
- app
- );
-
gtk_widget_show(GTK_WIDGET(handle->main_window));
g_signal_connect(
@@ -464,8 +291,16 @@ ui_messenger_init(MESSENGER_Application *app,
);
}
+static void
+_free_ui_chat_entry (gpointer user_data)
+{
+ UI_CHAT_ENTRY_Handle* handle = (UI_CHAT_ENTRY_Handle*) user_data;
+
+ ui_chat_entry_delete(handle);
+}
+
void
-ui_messenger_run(MESSENGER_Application *app)
+ui_messenger_cleanup(UI_MESSENGER_Handle *handle)
{
- ui_messenger_init(app, &(app->ui.messenger));
+ g_list_free_full(handle->chat_entries, _free_ui_chat_entry);
}
diff --git a/src/ui/messenger.h b/src/ui/messenger.h
@@ -33,6 +33,8 @@ typedef struct MESSENGER_Application MESSENGER_Application;
typedef struct UI_MESSENGER_Handle
{
+ GList *chat_entries;
+
GtkApplicationWindow *main_window;
HdyLeaflet *leaflet_chat;
@@ -40,7 +42,6 @@ typedef struct UI_MESSENGER_Handle
HdyFlap *flap_chat_details;
HdyHeaderBar *title_bar;
- GtkButton *back_button;
HdyAvatar *profile_avatar;
GtkLabel *profile_label;
@@ -63,19 +64,9 @@ typedef struct UI_MESSENGER_Handle
GtkSearchEntry *chats_search;
GtkListBox *chats_listbox;
- GtkLabel *chat_title;
- GtkLabel *chat_subtitle;
- GtkButton *chat_details_button;
+ GtkStack *chats_stack;
GtkButton *hide_chat_details_button;
-
- GtkListBox *messages_listbox;
-
- GtkButton *attach_file_button;
- GtkTextView *send_text_view;
- GtkButton *emoji_button;
- GtkButton *send_record_button;
- GtkImage *send_record_symbol;
} UI_MESSENGER_Handle;
void
@@ -83,6 +74,6 @@ ui_messenger_init(MESSENGER_Application *app,
UI_MESSENGER_Handle *handle);
void
-ui_messenger_run(MESSENGER_Application *app);
+ui_messenger_cleanup(UI_MESSENGER_Handle *handle);
#endif /* UI_MESSENGER_H_ */