commit f52675513361ac166800d2216969ab370c845318
parent 5481b37fcf1a820e55719da9593216e58774da18
Author: TheJackiMonster <thejackimonster@gmail.com>
Date: Thu, 17 Mar 2022 13:05:06 +0100
Implemented contact details dialog
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
Diffstat:
5 files changed, 426 insertions(+), 26 deletions(-)
diff --git a/resources/ui/contact_info.ui b/resources/ui/contact_info.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2
-Copyright (C) 2021‑‑2022 GNUnet e.V.
+Copyright (C) 2022 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
@@ -25,6 +25,7 @@ Author: Tobias Frisch
<requires lib="libhandy" version="1.2"/>
<object class="GtkDialog" id="contact_info_dialog">
<property name="can-focus">False</property>
+ <property name="title" translatable="yes">Contact Information</property>
<property name="modal">True</property>
<property name="window-position">center-on-parent</property>
<property name="type-hint">dialog</property>
@@ -40,7 +41,6 @@ Author: Tobias Frisch
<child>
<object class="GtkButton" id="back_button">
<property name="label" translatable="yes">Back</property>
- <property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
</object>
@@ -114,10 +114,42 @@ Author: Tobias Frisch
</packing>
</child>
<child>
- <object class="GtkEntry" id="contact_name">
- <property name="width-request">250</property>
+ <object class="GtkBox">
<property name="visible">True</property>
- <property name="can-focus">True</property>
+ <property name="can-focus">False</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkEntry" id="contact_name">
+ <property name="width-request">210</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can-focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="contact_edit_button">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <child>
+ <object class="GtkImage" id="contact_edit_symbol">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">document-edit-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">False</property>
@@ -161,6 +193,10 @@ Author: Tobias Frisch
<property name="height-request">250</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
+ <property name="margin-start">16</property>
+ <property name="margin-end">16</property>
+ <property name="margin-top">16</property>
+ <property name="margin-bottom">16</property>
</object>
<packing>
<property name="name">drawing_page</property>
diff --git a/src/application.h b/src/application.h
@@ -32,6 +32,7 @@
#include "ui/about.h"
#include "ui/accounts.h"
+#include "ui/contact_info.h"
#include "ui/contacts.h"
#include "ui/delete_messages.h"
#include "ui/invite_contact.h"
@@ -94,9 +95,10 @@ typedef struct MESSENGER_Application
UI_MESSENGER_Handle messenger;
UI_ABOUT_Handle about;
+ UI_CONTACT_INFO_Handle contact_info;
+ UI_DELETE_MESSAGES_Handle delete_messages;
UI_INVITE_CONTACT_Handle invite_contact;
UI_SEND_FILE_Handle send_file;
- UI_DELETE_MESSAGES_Handle delete_messages;
UI_NEW_CONTACT_Handle new_contact;
UI_NEW_GROUP_Handle new_group;
diff --git a/src/ui/chat.c b/src/ui/chat.c
@@ -77,7 +77,8 @@ handle_chat_contacts_listbox_row_activated(GtkListBox *listbox,
GtkListBoxRow *row,
gpointer user_data)
{
- MESSENGER_Application *app = (MESSENGER_Application*) user_data;
+ UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data;
+ MESSENGER_Application *app = handle->app;
GtkTextView *text_view = GTK_TEXT_VIEW(
g_object_get_qdata(G_OBJECT(listbox), app->quarks.widget)
@@ -104,19 +105,15 @@ handle_chat_contacts_listbox_row_activated(GtkListBox *listbox,
g_object_get_qdata(G_OBJECT(row), app->quarks.data)
);
- if ((!contact) || (!GNUNET_CHAT_contact_get_key(contact)) ||
- (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact)))
+ if (!contact)
return;
- struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context(
- contact
- );
+ hdy_flap_set_reveal_flap(handle->flap_chat_details, FALSE);
- if (!context)
- return;
+ ui_contact_info_dialog_init(app, &(app->ui.contact_info));
+ ui_contact_info_dialog_update(&(app->ui.contact_info), contact, FALSE);
- if (GNUNET_SYSERR == GNUNET_CHAT_context_get_status(context))
- GNUNET_CHAT_context_request(context);
+ gtk_widget_show(GTK_WIDGET(app->ui.contact_info.dialog));
}
static void
@@ -156,12 +153,25 @@ handle_back_button_click(UNUSED GtkButton *button,
}
static void
-handle_reveal_identity_button_click(UNUSED GtkButton *button,
+handle_reveal_identity_button_click(GtkButton *button,
gpointer user_data)
{
UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data;
+ MESSENGER_Application *app = handle->app;
+
+ struct GNUNET_CHAT_Contact *contact = (struct GNUNET_CHAT_Contact*) (
+ g_object_get_qdata(G_OBJECT(button), app->quarks.data)
+ );
- // TODO
+ if (!contact)
+ return;
+
+ hdy_flap_set_reveal_flap(handle->flap_chat_details, FALSE);
+
+ ui_contact_info_dialog_init(app, &(app->ui.contact_info));
+ ui_contact_info_dialog_update(&(app->ui.contact_info), contact, TRUE);
+
+ gtk_widget_show(GTK_WIDGET(app->ui.contact_info.dialog));
}
static void
@@ -1197,7 +1207,7 @@ ui_chat_new(MESSENGER_Application *app)
handle->chat_contacts_listbox,
"row-activated",
G_CALLBACK(handle_chat_contacts_listbox_row_activated),
- app
+ handle
);
handle->chat_files_listbox = GTK_LIST_BOX(
@@ -1443,8 +1453,8 @@ ui_chat_update(UI_CHAT_Handle *handle,
{
GNUNET_assert((handle) && (app) && (context));
- const struct GNUNET_CHAT_Contact* contact;
- const struct GNUNET_CHAT_Group* group;
+ struct GNUNET_CHAT_Contact* contact;
+ struct GNUNET_CHAT_Group* group;
contact = GNUNET_CHAT_context_get_contact(context);
group = GNUNET_CHAT_context_get_group(context);
@@ -1524,6 +1534,12 @@ ui_chat_update(UI_CHAT_Handle *handle,
group? TRUE : FALSE
);
+ g_object_set_qdata(
+ G_OBJECT(handle->reveal_identity_button),
+ app->quarks.data,
+ contact
+ );
+
gtk_widget_set_visible(
GTK_WIDGET(handle->reveal_identity_button),
contact? TRUE : FALSE
diff --git a/src/ui/contact_info.c b/src/ui/contact_info.c
@@ -24,9 +24,137 @@
#include "contact_info.h"
+#include "chat_entry.h"
#include "../application.h"
static void
+handle_contact_edit_button_click(UNUSED GtkButton *button,
+ gpointer user_data)
+{
+ UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
+
+ gboolean editable = gtk_widget_is_sensitive(
+ GTK_WIDGET(handle->contact_name_entry)
+ );
+
+ struct GNUNET_CHAT_Contact *contact = (struct GNUNET_CHAT_Contact*) (
+ g_object_get_qdata(
+ G_OBJECT(handle->contact_name_entry),
+ handle->app->quarks.data
+ )
+ );
+
+ const gchar *name = gtk_entry_get_text(handle->contact_name_entry);
+
+ if ((editable) && (contact))
+ GNUNET_CHAT_contact_set_name(
+ contact,
+ (name) && (g_utf8_strlen(name, 1))? name : NULL
+ );
+
+ gtk_image_set_from_icon_name(
+ handle->contact_edit_symbol,
+ editable?
+ "document-edit-symbolic" :
+ "emblem-ok-symbolic",
+ GTK_ICON_SIZE_BUTTON
+ );
+
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(handle->contact_name_entry),
+ !editable
+ );
+}
+
+static void
+handle_contact_name_entry_activate(UNUSED GtkEntry *entry,
+ gpointer user_data)
+{
+ UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
+
+ handle_contact_edit_button_click(handle->contact_edit_button, handle);
+}
+
+static void
+_contact_info_reveal_identity(UI_CONTACT_INFO_Handle *handle)
+{
+ gtk_widget_set_visible(GTK_WIDGET(handle->back_button), TRUE);
+
+ gtk_stack_set_visible_child(
+ handle->contact_info_stack,
+ GTK_WIDGET(handle->id_drawing_area)
+ );
+}
+
+static void
+handle_reveal_identity_button_click(UNUSED GtkButton *button,
+ gpointer user_data)
+{
+ _contact_info_reveal_identity((UI_CONTACT_INFO_Handle*) user_data);
+}
+
+static void
+handle_open_chat_button_click(UNUSED GtkButton *button,
+ gpointer user_data)
+{
+ UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
+
+ struct GNUNET_CHAT_Contact *contact = (struct GNUNET_CHAT_Contact*) (
+ g_object_get_qdata(
+ G_OBJECT(handle->contact_name_entry),
+ handle->app->quarks.data
+ )
+ );
+
+ if (!contact)
+ return;
+
+ struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context(
+ contact
+ );
+
+ if (!context)
+ return;
+
+ if (GNUNET_SYSERR == GNUNET_CHAT_context_get_status(context))
+ {
+ GNUNET_CHAT_context_request(context);
+ goto close_dialog;
+ }
+
+ UI_CHAT_ENTRY_Handle *entry = GNUNET_CHAT_context_get_user_pointer(context);
+
+ if (!entry)
+ return;
+
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW(
+ gtk_widget_get_parent(entry->entry_box)
+ );
+
+ gtk_list_box_select_row(handle->app->ui.messenger.chats_listbox, row);
+ gtk_list_box_invalidate_filter(handle->app->ui.messenger.chats_listbox);
+
+ gtk_widget_activate(GTK_WIDGET(row));
+
+close_dialog:
+ gtk_window_close(GTK_WINDOW(handle->dialog));
+}
+
+static void
+handle_back_button_click(UNUSED GtkButton *button,
+ gpointer user_data)
+{
+ UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
+
+ gtk_widget_set_visible(GTK_WIDGET(handle->back_button), FALSE);
+
+ gtk_stack_set_visible_child(
+ handle->contact_info_stack,
+ handle->details_box
+ );
+}
+
+static void
handle_close_button_click(UNUSED GtkButton *button,
gpointer user_data)
{
@@ -41,10 +169,106 @@ handle_dialog_destroy(UNUSED GtkWidget *window,
ui_contact_info_dialog_cleanup((UI_CONTACT_INFO_Handle*) user_data);
}
+static gboolean
+handle_id_drawing_area_draw(GtkWidget* drawing_area,
+ cairo_t* cairo,
+ gpointer user_data)
+{
+ UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
+
+ GtkStyleContext* context = gtk_widget_get_style_context(drawing_area);
+
+ if (!context)
+ return FALSE;
+
+ const guint width = gtk_widget_get_allocated_width(drawing_area);
+ const guint height = gtk_widget_get_allocated_height(drawing_area);
+
+ gtk_render_background(context, cairo, 0, 0, width, height);
+
+ if ((!(handle->qr)) || (handle->qr->width <= 0))
+ return FALSE;
+
+ const guint m = 3;
+ const guint w = handle->qr->width;
+ const guint w2 = w + m * 2;
+
+ guchar *pixels = (guchar*) g_malloc(sizeof(guchar) * w2 * w2 * 3);
+
+ guint x, y, z;
+ for (y = 0; y < w2; y++)
+ for (x = 0; x < w2; x++)
+ {
+ guchar value;
+
+ if ((x >= m) && (y >= m) && (x - m < w) && (y - m < w))
+ value = ((handle->qr->data[(y - m) * w + x - m]) & 1);
+ else
+ value = 0;
+
+ for (z = 0; z < 3; z++)
+ pixels[(y * w2 + x) * 3 + z] = value? 0x00 : 0xff;
+ }
+
+ GdkPixbuf *image = gdk_pixbuf_new_from_data(
+ pixels,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ w2,
+ w2,
+ w2 * 3,
+ NULL,
+ NULL
+ );
+
+ if (!image)
+ return FALSE;
+
+ int dwidth = gdk_pixbuf_get_width(image);
+ int dheight = gdk_pixbuf_get_height(image);
+
+ double ratio_width = 1.0 * width / dwidth;
+ double ratio_height = 1.0 * height / dheight;
+
+ const double ratio = ratio_width < ratio_height? ratio_width : ratio_height;
+
+ dwidth = (int) (dwidth * ratio);
+ dheight = (int) (dheight * ratio);
+
+ double dx = (width - dwidth) * 0.5;
+ double dy = (height - dheight) * 0.5;
+
+ const int interp_type = (ratio >= 1.0?
+ GDK_INTERP_NEAREST :
+ GDK_INTERP_BILINEAR
+ );
+
+ GdkPixbuf* scaled = gdk_pixbuf_scale_simple(
+ image,
+ dwidth,
+ dheight,
+ interp_type
+ );
+
+ gtk_render_icon(context, cairo, scaled, dx, dy);
+
+ cairo_fill(cairo);
+
+ g_object_unref(scaled);
+ g_object_unref(image);
+
+ g_free(pixels);
+
+ return FALSE;
+}
+
void
ui_contact_info_dialog_init(MESSENGER_Application *app,
UI_CONTACT_INFO_Handle *handle)
{
+ handle->app = app;
+
handle->builder = gtk_builder_new_from_resource(
application_get_resource_path(app, "ui/contact_info.ui")
);
@@ -70,26 +294,76 @@ ui_contact_info_dialog_init(MESSENGER_Application *app,
gtk_builder_get_object(handle->builder, "contact_avatar")
);
- handle->contact_name = GTK_ENTRY(
+ handle->contact_name_entry = GTK_ENTRY(
gtk_builder_get_object(handle->builder, "contact_name")
);
+ handle->contact_edit_button = GTK_BUTTON(
+ gtk_builder_get_object(handle->builder, "contact_edit_button")
+ );
+
+ handle->contact_edit_symbol = GTK_IMAGE(
+ gtk_builder_get_object(handle->builder, "contact_edit_symbol")
+ );
+
+ g_signal_connect(
+ handle->contact_name_entry,
+ "activate",
+ G_CALLBACK(handle_contact_name_entry_activate),
+ handle
+ );
+
+ g_signal_connect(
+ handle->contact_edit_button,
+ "clicked",
+ G_CALLBACK(handle_contact_edit_button_click),
+ handle
+ );
+
handle->reveal_identity_button = GTK_BUTTON(
gtk_builder_get_object(handle->builder, "reveal_identity_button")
);
+ g_signal_connect(
+ handle->reveal_identity_button,
+ "clicked",
+ G_CALLBACK(handle_reveal_identity_button_click),
+ handle
+ );
+
handle->open_chat_button = GTK_BUTTON(
gtk_builder_get_object(handle->builder, "open_chat_button")
);
+ g_signal_connect(
+ handle->open_chat_button,
+ "clicked",
+ G_CALLBACK(handle_open_chat_button_click),
+ handle
+ );
+
handle->id_drawing_area = GTK_DRAWING_AREA(
gtk_builder_get_object(handle->builder, "id_drawing_area")
);
+ handle->id_draw_signal = g_signal_connect(
+ handle->id_drawing_area,
+ "draw",
+ G_CALLBACK(handle_id_drawing_area_draw),
+ handle
+ );
+
handle->back_button = GTK_BUTTON(
gtk_builder_get_object(handle->builder, "back_button")
);
+ g_signal_connect(
+ handle->back_button,
+ "clicked",
+ G_CALLBACK(handle_back_button_click),
+ handle
+ );
+
handle->close_button = GTK_BUTTON(
gtk_builder_get_object(handle->builder, "close_button")
);
@@ -111,15 +385,74 @@ ui_contact_info_dialog_init(MESSENGER_Application *app,
void
ui_contact_info_dialog_update(UI_CONTACT_INFO_Handle *handle,
- struct GNUNET_CHAT_Contact *contact)
+ struct GNUNET_CHAT_Contact *contact,
+ gboolean reveal)
{
- // TODO
+ const char *name = GNUNET_CHAT_contact_get_name(contact);
+
+ hdy_avatar_set_text(handle->contact_avatar, name? name : "");
+ gtk_entry_set_text(handle->contact_name_entry, name? name : "");
+
+ g_object_set_qdata(
+ G_OBJECT(handle->contact_name_entry),
+ handle->app->quarks.data,
+ contact
+ );
+
+ const char *key = GNUNET_CHAT_contact_get_key(contact);
+
+ if (handle->qr)
+ QRcode_free(handle->qr);
+
+ if (key)
+ handle->qr = QRcode_encodeString(
+ key,
+ 0,
+ QR_ECLEVEL_L,
+ QR_MODE_8,
+ 0
+ );
+ else
+ handle->qr = NULL;
+
+ if (handle->id_drawing_area)
+ gtk_widget_queue_draw(GTK_WIDGET(handle->id_drawing_area));
+
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(handle->reveal_identity_button),
+ key? TRUE : FALSE
+ );
+
+ struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context(
+ contact
+ );
+
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(handle->open_chat_button),
+ context? TRUE : FALSE
+ );
+
+ gtk_widget_set_visible(
+ GTK_WIDGET(handle->open_chat_button),
+ GNUNET_YES != GNUNET_CHAT_contact_is_owned(contact)
+ );
+
+ if (reveal)
+ _contact_info_reveal_identity(handle);
}
void
ui_contact_info_dialog_cleanup(UI_CONTACT_INFO_Handle *handle)
{
+ g_signal_handler_disconnect(
+ handle->id_drawing_area,
+ handle->id_draw_signal
+ );
+
g_object_unref(handle->builder);
+ if (handle->qr)
+ QRcode_free(handle->qr);
+
memset(handle, 0, sizeof(*handle));
}
diff --git a/src/ui/contact_info.h b/src/ui/contact_info.h
@@ -27,8 +27,14 @@
#include "messenger.h"
+#include <cairo/cairo.h>
+#include <gdk/gdkpixbuf.h>
+#include <qrencode.h>
+
typedef struct UI_CONTACT_INFO_Handle
{
+ MESSENGER_Application *app;
+
GtkBuilder *builder;
GtkDialog *dialog;
@@ -36,15 +42,21 @@ typedef struct UI_CONTACT_INFO_Handle
GtkWidget *details_box;
HdyAvatar *contact_avatar;
- GtkEntry *contact_name;
+ GtkEntry *contact_name_entry;
+
+ GtkButton *contact_edit_button;
+ GtkImage *contact_edit_symbol;
GtkButton *reveal_identity_button;
GtkButton *open_chat_button;
GtkDrawingArea *id_drawing_area;
+ gulong id_draw_signal;
GtkButton *back_button;
GtkButton *close_button;
+
+ QRcode *qr;
} UI_CONTACT_INFO_Handle;
void
@@ -53,7 +65,8 @@ ui_contact_info_dialog_init(MESSENGER_Application *app,
void
ui_contact_info_dialog_update(UI_CONTACT_INFO_Handle *handle,
- struct GNUNET_CHAT_Contact *contact);
+ struct GNUNET_CHAT_Contact *contact,
+ gboolean reveal);
void
ui_contact_info_dialog_cleanup(UI_CONTACT_INFO_Handle *handle);