messenger-gtk

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

commit 673f8487f949bc5992592cea6762e6cc8366b0e9
parent a37289b476f36d9c8bffc56b9ffc93f091ac70d9
Author: Jacki <jacki@thejackimonster.de>
Date:   Fri, 22 Mar 2024 19:51:17 +0100

Implement dialog to add tags to messages

Signed-off-by: Jacki <jacki@thejackimonster.de>

Diffstat:
Mresources/ui.gresource.xml | 4+++-
Aresources/ui/new_tag.ui | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresources/ui/tag.ui | 34++++++++++++++++++++++++++++++++++
Msrc/application.h | 4+++-
Msrc/ui.c | 19+++++++++++++++++++
Msrc/ui.h | 9+++++++++
Msrc/ui/chat.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/ui/meson.build | 2++
Msrc/ui/message.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/ui/message.h | 4+++-
Asrc/ui/new_tag.c | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ui/new_tag.h | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ui/tag.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ui/tag.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
14 files changed, 816 insertions(+), 25 deletions(-)

diff --git a/resources/ui.gresource.xml b/resources/ui.gresource.xml @@ -16,15 +16,17 @@ <file compressed="true">ui/message-sent.ui</file> <file compressed="true">ui/message-status.ui</file> <file compressed="true">ui/messenger.ui</file> + <file compressed="true">ui/new_account.ui</file> <file compressed="true">ui/new_contact.ui</file> <file compressed="true">ui/new_group.ui</file> <file compressed="true">ui/new_lobby.ui</file> <file compressed="true">ui/new_platform.ui</file> - <file compressed="true">ui/new_account.ui</file> + <file compressed="true">ui/new_tag.ui</file> <file compressed="true">ui/picker.ui</file> <file compressed="true">ui/play_media.ui</file> <file compressed="true">ui/account_entry.ui</file> <file compressed="true">ui/send_file.ui</file> <file compressed="true">ui/settings.ui</file> + <file compressed="true">ui/tag.ui</file> </gresource> </gresources> diff --git a/resources/ui/new_tag.ui b/resources/ui/new_tag.ui @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.40.0 + +Copyright (C) 2024 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_tag_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="confirm_button"> + <property name="label" translatable="yes">Confirm</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">1</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="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="border-width">8</property> + <property name="orientation">vertical</property> + <property name="spacing">4</property> + <child> + <object class="HdyAvatar" id="tag_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">user-bookmarks-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">Tag:</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="tag_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> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/resources/ui/tag.ui b/resources/ui/tag.ui @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.40.0 + +Copyright (C) 2024 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="GtkLabel" id="tag_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="ellipsize">end</property> + <property name="single-line-mode">True</property> + <style> + <class name="tag-label"/> + </style> + </object> +</interface> diff --git a/src/application.h b/src/application.h @@ -48,6 +48,7 @@ #include "ui/new_group.h" #include "ui/new_lobby.h" #include "ui/new_platform.h" +#include "ui/new_tag.h" #include "ui/play_media.h" #include "ui/send_file.h" #include "ui/settings.h" @@ -124,11 +125,12 @@ typedef struct MESSENGER_Application UI_SEND_FILE_Handle send_file; UI_PLAY_MEDIA_Handle play_media; + UI_NEW_ACCOUNT_Handle new_account; 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; + UI_NEW_TAG_Handle new_tag; UI_ACCOUNTS_Handle accounts; UI_CONTACTS_Handle contacts; diff --git a/src/ui.c b/src/ui.c @@ -24,6 +24,8 @@ #include "ui.h" +#include <gnunet/gnunet_common.h> + void ui_label_set_text(GtkLabel *label, const char *text) { @@ -56,6 +58,23 @@ ui_entry_set_text(GtkEntry *entry, const char *text) g_free(_text); } +char* +ui_entry_get_text(GtkEntry *entry) +{ + g_assert(entry); + + const gchar *text = gtk_entry_get_text(entry); + + if (!text) + return NULL; + + gchar *_text = g_locale_from_utf8(text, -1, NULL, NULL, NULL); + char *result = GNUNET_strdup(_text); + g_free(_text); + + return result; +} + void ui_avatar_set_text(HdyAvatar *avatar, const char *text) { diff --git a/src/ui.h b/src/ui.h @@ -51,6 +51,15 @@ ui_entry_set_text(GtkEntry *entry, const char *text); /** + * Returns the text from a GtkEntry applying automatic + * inverse utf8 conversion. + * + * @param entry Entry + */ +char* +ui_entry_get_text(GtkEntry *entry); + +/** * Sets the text of a HdyAvatar applying automatic utf8 * conversion. * diff --git a/src/ui/chat.c b/src/ui/chat.c @@ -334,12 +334,8 @@ _iterate_message_tags(void *cls, if (!_text) return GNUNET_YES; - gchar *match = g_strstr_len(_text, -1, filterTags->filter); - if (match) - { + if (g_strstr_len(_text, -1, filterTags->filter)) filterTags->matching = TRUE; - g_free(match); - } g_free(_text); return filterTags->matching? GNUNET_NO : GNUNET_YES; @@ -390,7 +386,7 @@ handle_chat_messages_filter(GtkListBoxRow *row, if (text) result |= g_str_match_string(filter, text, TRUE); - if (('#' == filter[0]) && (message->msg)) + if (('#' == *filter) && (message->msg)) { struct FilterTags filterTags; filterTags.filter = &(filter[1]); @@ -454,6 +450,94 @@ handle_chat_selection_close_button_click(UNUSED GtkButton *button, } void +_new_tag_callback(MESSENGER_Application *app, + GList *selected, + const char *tag) +{ + g_assert(app); + + GtkTextView *text_view = GTK_TEXT_VIEW(g_object_get_qdata( + G_OBJECT(app->ui.new_tag.dialog), + app->quarks.widget + )); + + if (!text_view) + return; + + struct GNUNET_CHAT_Context *context = (struct GNUNET_CHAT_Context*) ( + g_object_get_qdata( + G_OBJECT(text_view), + app->quarks.data + ) + ); + + UI_MESSAGE_Handle *message; + if ((!context) || (!tag)) + goto cleanup; + + while (selected) + { + GtkListBoxRow *row = GTK_LIST_BOX_ROW(selected->data); + + if (!row) + goto skip_row; + + message = (UI_MESSAGE_Handle*) g_object_get_qdata( + G_OBJECT(row), + app->quarks.ui + ); + + if ((!message) || (!(message->msg))) + goto skip_row; + + GNUNET_CHAT_context_send_tag( + context, + message->msg, + tag + ); + + skip_row: + selected = selected->next; + } + +cleanup: + g_object_set_qdata( + G_OBJECT(app->ui.new_tag.dialog), + app->quarks.widget, + NULL + ); +} + +static void +handle_chat_selection_tag_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + g_assert(user_data); + + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; + + MESSENGER_Application *app = handle->app; + + GList *selected = gtk_list_box_get_selected_rows(handle->messages_listbox); + + ui_new_tag_dialog_init(app, &(app->ui.new_tag)); + + g_object_set_qdata( + G_OBJECT(app->ui.new_tag.dialog), + app->quarks.widget, + handle->send_text_view + ); + + ui_new_tag_dialog_link( + &(app->ui.new_tag), + _new_tag_callback, + selected + ); + + gtk_widget_show(GTK_WIDGET(app->ui.new_tag.dialog)); +} + +void _delete_messages_callback(MESSENGER_Application *app, GList *selected, gulong delay) @@ -1528,6 +1612,13 @@ ui_chat_new(MESSENGER_Application *app) ); g_signal_connect( + handle->selection_tag_button, + "clicked", + G_CALLBACK(handle_chat_selection_tag_button_click), + handle + ); + + g_signal_connect( handle->selection_delete_button, "clicked", G_CALLBACK(handle_chat_selection_delete_button_click), @@ -2003,7 +2094,7 @@ ui_chat_add_message(UI_CHAT_Handle *handle, void ui_chat_remove_message(UI_CHAT_Handle *handle, - UNUSED MESSENGER_Application *app, + MESSENGER_Application *app, UI_MESSAGE_Handle *message) { g_assert((handle) && (message) && (message->message_box)); @@ -2016,7 +2107,7 @@ ui_chat_remove_message(UI_CHAT_Handle *handle, if (parent == GTK_WIDGET(handle->messages_listbox)) gtk_container_remove(GTK_CONTAINER(handle->messages_listbox), row); - ui_message_delete(message); + ui_message_delete(message, app); } void diff --git a/src/ui/meson.build b/src/ui/meson.build @@ -37,8 +37,10 @@ messenger_gtk_ui_sources = files([ 'new_group.c', 'new_group.h', 'new_lobby.c', 'new_lobby.h', 'new_platform.c', 'new_platform.h', + 'new_tag.c', 'new_tag.h', 'picker.c', 'picker.h', 'play_media.c', 'play_media.h', 'send_file.c', 'send_file.h', 'settings.c', 'settings.h', + 'tag.c', 'tag.h', ]) diff --git a/src/ui/message.c b/src/ui/message.c @@ -27,6 +27,8 @@ #include <gnunet/gnunet_chat_lib.h> #include <gnunet/gnunet_common.h> +#include "tag.h" + #include "../application.h" #include "../contact.h" #include "../file.h" @@ -796,21 +798,42 @@ ui_message_add_tag(UI_MESSAGE_Handle *handle, const char *tag_value = GNUNET_CHAT_message_get_text(tag_message); - if ((!tag_value) || (!tag_value[0])) + if ((!tag_value) || (!*tag_value)) return; - GtkLabel *tag_label = GTK_LABEL(gtk_label_new(NULL)); + UI_TAG_Handle *tag = ui_tag_new(app); + ui_tag_set_message(tag, app, tag_message); - if (!tag_label) - return; + gtk_container_add(GTK_CONTAINER(handle->tag_flow_box), GTK_WIDGET(tag->tag_label)); + gtk_widget_show_all(GTK_WIDGET(tag->tag_label)); +} - ui_label_set_text(tag_label, tag_value); - gtk_label_set_ellipsize(tag_label, PANGO_ELLIPSIZE_END); +static void +_remove_tag_from_message(UI_MESSAGE_Handle *handle, + MESSENGER_Application *app, + GtkWidget *child) +{ + g_assert((handle) && (app) && (child)); + + GList *items = gtk_container_get_children(GTK_CONTAINER(child)); + UI_TAG_Handle *tag = NULL; - g_object_set_qdata(G_OBJECT(tag_label), app->quarks.data, (gpointer) tag_message); + if (items) + { + GtkLabel *tag_label = GTK_LABEL(items->data); - gtk_container_add(GTK_CONTAINER(handle->tag_flow_box), GTK_WIDGET(tag_label)); - gtk_widget_show_all(GTK_WIDGET(tag_label)); + tag = g_object_get_qdata( + G_OBJECT(tag_label), + app->quarks.ui + ); + + g_list_free(items); + } + + gtk_container_remove(GTK_CONTAINER(handle->tag_flow_box), child); + + ui_tag_delete(tag); + gtk_widget_destroy(child); } void @@ -858,22 +881,39 @@ ui_message_remove_tag(UI_MESSAGE_Handle *handle, break; } - g_list_free(children); + if (children) + g_list_free(children); if (!removable) return; - gtk_container_remove(GTK_CONTAINER(handle->tag_flow_box), removable); - gtk_widget_destroy(removable); + _remove_tag_from_message(handle, app, removable); } void -ui_message_delete(UI_MESSAGE_Handle *handle) +ui_message_delete(UI_MESSAGE_Handle *handle, + MESSENGER_Application *app) { - g_assert(handle); + g_assert((handle) && (app)); ui_message_set_contact(handle, NULL); + GList *children = gtk_container_get_children(GTK_CONTAINER(handle->tag_flow_box)); + + GList *list = children; + while (list) + { + GtkWidget *child = GTK_WIDGET(list->data); + + if (child) + _remove_tag_from_message(handle, app, child); + + list = list->next; + } + + if (children) + g_list_free(children); + _clear_message_preview_data(handle); g_object_unref(handle->builder[1]); diff --git a/src/ui/message.h b/src/ui/message.h @@ -169,8 +169,10 @@ ui_message_remove_tag(UI_MESSAGE_Handle *handle, * message handle. * * @param handle Message handle + * @param app Messenger application */ void -ui_message_delete(UI_MESSAGE_Handle *handle); +ui_message_delete(UI_MESSAGE_Handle *handle, + MESSENGER_Application *app); #endif /* UI_MESSAGE_H_ */ diff --git a/src/ui/new_tag.c b/src/ui/new_tag.c @@ -0,0 +1,210 @@ +/* + This file is part of GNUnet. + Copyright (C) 2024 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_tag.c + */ + +#include "new_tag.h" + +#include "../application.h" +#include "../ui.h" + +#include <gnunet/gnunet_common.h> + +static void +_add_new_tag(MESSENGER_Application *app, + UI_NEW_TAG_Handle *handle) +{ + g_assert((app) && (handle)); + + char *tag = ui_entry_get_text(handle->tag_entry); + + if (handle->callback) + handle->callback( + app, + handle->selected, + tag + ); + + if (tag) + GNUNET_free(tag); + + gtk_window_close(GTK_WINDOW(handle->dialog)); +} + +static void +handle_tag_entry_changed(GtkEditable *editable, + gpointer user_data) +{ + g_assert((editable) && (user_data)); + + HdyAvatar *avatar = HDY_AVATAR(user_data); + GtkEntry *entry = GTK_ENTRY(editable); + + const gchar *text = gtk_entry_get_text(entry); + + hdy_avatar_set_text(avatar, text); +} + +static void +handle_tag_entry_activate(UNUSED GtkEntry *entry, + gpointer user_data) +{ + g_assert(user_data); + + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + _add_new_tag(app, &(app->ui.new_tag)); + + gtk_window_close(GTK_WINDOW(app->ui.new_tag.dialog)); +} + +static void +handle_cancel_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + g_assert(user_data); + + GtkDialog *dialog = GTK_DIALOG(user_data); + gtk_window_close(GTK_WINDOW(dialog)); +} + +static void +handle_confirm_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + g_assert(user_data); + + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + _add_new_tag(app, &(app->ui.new_tag)); + + gtk_window_close(GTK_WINDOW(app->ui.new_tag.dialog)); +} + +static void +handle_dialog_destroy(UNUSED GtkWidget *window, + gpointer user_data) +{ + g_assert(user_data); + + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + ui_new_tag_dialog_cleanup(&(app->ui.new_tag)); +} + +void +ui_new_tag_dialog_init(MESSENGER_Application *app, + UI_NEW_TAG_Handle *handle) +{ + g_assert((app) && (handle)); + + handle->selected = NULL; + handle->callback = NULL; + + handle->builder = gtk_builder_new_from_resource( + application_get_resource_path(app, "ui/new_tag.ui") + ); + + handle->dialog = GTK_DIALOG( + gtk_builder_get_object(handle->builder, "new_tag_dialog") + ); + + gtk_window_set_transient_for( + GTK_WINDOW(handle->dialog), + GTK_WINDOW(app->ui.messenger.main_window) + ); + + handle->tag_avatar = HDY_AVATAR( + gtk_builder_get_object(handle->builder, "tag_avatar") + ); + + handle->tag_entry = GTK_ENTRY( + gtk_builder_get_object(handle->builder, "tag_entry") + ); + + g_signal_connect( + handle->tag_entry, + "changed", + G_CALLBACK(handle_tag_entry_changed), + handle->tag_avatar + ); + + g_signal_connect( + handle->tag_entry, + "activate", + G_CALLBACK(handle_tag_entry_activate), + app + ); + + 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->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), + app + ); +} + +void +ui_new_tag_dialog_link(UI_NEW_TAG_Handle *handle, + UI_NEW_TAG_Callback callback, + GList *selected) +{ + g_assert((handle) && (callback)); + + handle->selected = selected; + handle->callback = callback; +} + +void +ui_new_tag_dialog_cleanup(UI_NEW_TAG_Handle *handle) +{ + g_assert(handle); + + if (handle->selected) + g_list_free(handle->selected); + + g_object_unref(handle->builder); + + memset(handle, 0, sizeof(*handle)); +} diff --git a/src/ui/new_tag.h b/src/ui/new_tag.h @@ -0,0 +1,87 @@ +/* + This file is part of GNUnet. + Copyright (C) 2024 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_tag.h + */ + +#ifndef UI_NEW_TAG_H_ +#define UI_NEW_TAG_H_ + +#include "messenger.h" + +typedef void +(*UI_NEW_TAG_Callback) ( + MESSENGER_Application *app, + GList *selected, + const char *tag +); + +typedef struct UI_NEW_TAG_Handle +{ + GList *selected; + + UI_NEW_TAG_Callback callback; + + GtkBuilder *builder; + GtkDialog *dialog; + + HdyAvatar *tag_avatar; + GtkEntry *tag_entry; + + GtkButton *cancel_button; + GtkButton *confirm_button; +} UI_NEW_TAG_Handle; + +/** + * Initializes a handle for the new tag dialog + * of a given messenger application. + * + * @param app Messenger application + * @param handle New tag dialog handle + */ +void +ui_new_tag_dialog_init(MESSENGER_Application *app, + UI_NEW_TAG_Handle *handle); + +/** + * Links a custom list and a callback to a + * given new tag dialog which will be used + * to handle the event of tagging. + * + * @param handle New tag dialog handle + * @param callback New tag callback + * @param selected Selected messages + */ +void +ui_new_tag_dialog_link(UI_NEW_TAG_Handle *handle, + UI_NEW_TAG_Callback callback, + GList *selected); + +/** + * Cleans up the allocated resources and resets the + * state of a given new tag dialog handle. + * + * @param handle New tag dialog handle + */ +void +ui_new_tag_dialog_cleanup(UI_NEW_TAG_Handle *handle); + +#endif /* UI_NEW_TAG_H_ */ diff --git a/src/ui/tag.c b/src/ui/tag.c @@ -0,0 +1,89 @@ +/* + This file is part of GNUnet. + Copyright (C) 2024 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/tag.c + */ + +#include "tag.h" + +#include "../application.h" +#include "../ui.h" + +UI_TAG_Handle* +ui_tag_new(MESSENGER_Application *app) +{ + g_assert(app); + + UI_TAG_Handle* handle = g_malloc(sizeof(UI_TAG_Handle)); + + memset(handle, 0, sizeof(*handle)); + + handle->builder = gtk_builder_new_from_resource( + application_get_resource_path(app, "ui/tag.ui") + ); + + handle->tag_label = GTK_LABEL( + gtk_builder_get_object(handle->builder, "tag_label") + ); + + g_object_set_qdata( + G_OBJECT(handle->tag_label), + app->quarks.ui, + handle + ); + + return handle; +} + +void +ui_tag_set_message(UI_TAG_Handle* handle, + MESSENGER_Application *app, + const struct GNUNET_CHAT_Message *message) +{ + g_assert((handle) && (message)); + + if (GNUNET_CHAT_KIND_TAG != GNUNET_CHAT_message_get_kind(message)) + return; + + const char *tag_value = GNUNET_CHAT_message_get_text(message); + + GString *label = g_string_new("#"); + g_string_append(label, tag_value); + + ui_label_set_text(handle->tag_label, label->str); + g_string_free(label, TRUE); + + g_object_set_qdata( + G_OBJECT(handle->tag_label), + app->quarks.data, + (gpointer) message + ); +} + +void +ui_tag_delete(UI_TAG_Handle *handle) +{ + g_assert(handle); + + g_object_unref(handle->builder); + + g_free(handle); +} diff --git a/src/ui/tag.h b/src/ui/tag.h @@ -0,0 +1,71 @@ +/* + This file is part of GNUnet. + Copyright (C) 2024 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/tag.h + */ + +#ifndef UI_TAG_H_ +#define UI_TAG_H_ + +#include "messenger.h" + +typedef struct UI_TAG_Handle +{ + GtkBuilder *builder; + + GtkLabel *tag_label; +} UI_TAG_Handle; + +/** + * Allocates and creates a new tag handle + * to manage a tag in a list for a given + * messenger application. + * + * @param app Messenger application + * @return New tag handle + */ +UI_TAG_Handle* +ui_tag_new(MESSENGER_Application *app); + +/** + * Sets the content of the given tag handle + * respectively to visually represent a + * selected chat tag message. + * + * @param handle Tag handle + * @param app Messenger application + * @param message Chat tag message + */ +void +ui_tag_set_message(UI_TAG_Handle* handle, + MESSENGER_Application *app, + const struct GNUNET_CHAT_Message *message); + +/** + * Frees its resources and destroys a given + * tag handle. + * + * @param handle Tag handle + */ +void +ui_tag_delete(UI_TAG_Handle *handle); + +#endif /* UI_TAG_H_ */