blob: 4391f11938f674092d66fcaa1737b3a79722c375 [file] [log] [blame]
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04001/*
Guillaume Roguez77c579d2018-01-30 15:54:02 -05002 * Copyright (C) 2015-2018 Savoir-faire Linux Inc.
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
Sébastien Blin55bff9d2017-10-03 15:15:23 -04004 * Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>
5 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "ringwelcomeview.h"
23
24#include <gtk/gtk.h>
25#include <glib/gi18n.h>
aviaudd355ba2016-04-08 14:23:09 -040026#include <qrencode.h>
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040027
Nicolas Jager15a8b902017-03-21 07:53:06 -040028// Qt
29#include <QObject>
30#include <QItemSelectionModel>
31
Nicolas Jagerf71f8d82017-04-17 10:22:06 -040032// LRC
Sébastien Blin55bff9d2017-10-03 15:15:23 -040033#include "utils/accounts.h"
Nicolas Jagerf71f8d82017-04-17 10:22:06 -040034
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040035struct _RingWelcomeView
36{
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050037 GtkScrolledWindow parent;
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040038};
39
40struct _RingWelcomeViewClass
41{
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050042 GtkScrolledWindowClass parent_class;
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040043};
44
45typedef struct _RingWelcomeViewPrivate RingWelcomeViewPrivate;
46
47struct _RingWelcomeViewPrivate
48{
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040049 GtkWidget *box_overlay;
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040050 GtkWidget *label_ringid;
51 GtkWidget *label_explanation;
52 GtkWidget *button_qrcode;
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040053 GtkWidget *revealer_qrcode;
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040054
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040055 AccountInfoPointer const *accountInfo_;
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040056};
57
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050058G_DEFINE_TYPE_WITH_PRIVATE(RingWelcomeView, ring_welcome_view, GTK_TYPE_SCROLLED_WINDOW);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040059
60#define RING_WELCOME_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_WELCOME_VIEW_TYPE, RingWelcomeViewPrivate))
61
Sébastien Blin55bff9d2017-10-03 15:15:23 -040062static gboolean draw_qrcode(GtkWidget*,cairo_t*,RingWelcomeView*);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040063static void switch_qrcode(RingWelcomeView* self);
aviaudd355ba2016-04-08 14:23:09 -040064
Sébastien Blin55bff9d2017-10-03 15:15:23 -040065void
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040066ring_welcome_update_view(RingWelcomeView* self) {
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040067 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040068
Sébastien Blin55bff9d2017-10-03 15:15:23 -040069 // Only draw a basic view for SIP accounts
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040070 if (not *priv->accountInfo_ || (*priv->accountInfo_)->profileInfo.type == lrc::api::profile::Type::SIP) {
Nicolas Jager15a8b902017-03-21 07:53:06 -040071 gtk_widget_hide(priv->button_qrcode);
Sébastien Blin55bff9d2017-10-03 15:15:23 -040072 gtk_widget_hide(priv->label_ringid);
73 gtk_widget_hide(priv->label_explanation);
Nicolas Jager15a8b902017-03-21 07:53:06 -040074 gtk_widget_hide(priv->revealer_qrcode);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040075 gtk_widget_set_opacity(priv->box_overlay, 1.0);
Sébastien Blin55bff9d2017-10-03 15:15:23 -040076 gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer_qrcode), FALSE);
77 return;
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040078 }
Sébastien Blin55bff9d2017-10-03 15:15:23 -040079
80 // Get registeredName, else the Ring Id
81 gchar *ring_id = nullptr;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040082 if(! (*priv->accountInfo_)->registeredName.empty()){
Sébastien Blin55bff9d2017-10-03 15:15:23 -040083 gtk_label_set_text(
84 GTK_LABEL(priv->label_explanation),
85 _("This is your Ring username.\nCopy and share it with your friends!")
86 );
87 ring_id = g_markup_printf_escaped("<span fgcolor=\"black\">ring:%s</span>",
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040088 (*priv->accountInfo_)->registeredName.c_str());
Sébastien Blin55bff9d2017-10-03 15:15:23 -040089 }
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040090 else if (!(*priv->accountInfo_)->profileInfo.uri.empty()) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -040091 gtk_label_set_text(
92 GTK_LABEL(priv->label_explanation),
93 C_("Do not translate \"RingID\"", "This is your RingID.\nCopy and share it with your friends!")
94 );
95 ring_id = g_markup_printf_escaped("<span fgcolor=\"black\">%s</span>",
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040096 (*priv->accountInfo_)->profileInfo.uri.c_str());
Sébastien Blin55bff9d2017-10-03 15:15:23 -040097 } else {
98 gtk_label_set_text(GTK_LABEL(priv->label_explanation), NULL);
Sébastien Blin1140f872018-02-26 10:12:16 -050099 ring_id = {};
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400100 }
101
102 gtk_label_set_markup(GTK_LABEL(priv->label_ringid), ring_id);
103
104 gtk_widget_show(priv->label_explanation);
105 gtk_widget_show(priv->label_ringid);
106 gtk_widget_show(priv->button_qrcode);
107 gtk_widget_show(priv->revealer_qrcode);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400108}
109
110static void
111ring_welcome_view_init(RingWelcomeView *self)
112{
113 RingWelcomeViewPrivate *priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
114
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -0500115 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
116
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400117 auto box_main = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);
118 gtk_container_add(GTK_CONTAINER(self), box_main);
119 gtk_box_set_baseline_position(GTK_BOX(box_main), GTK_BASELINE_POSITION_CENTER);
120 gtk_widget_set_vexpand(GTK_WIDGET(box_main), TRUE);
121 gtk_widget_set_hexpand(GTK_WIDGET(box_main), FALSE);
122 gtk_widget_set_valign(GTK_WIDGET(box_main), GTK_ALIGN_CENTER);
123 gtk_widget_set_halign(GTK_WIDGET(box_main), GTK_ALIGN_CENTER);
124 gtk_widget_set_visible(GTK_WIDGET(box_main), TRUE);
125
126 /* overlay to show hide the qr code over the stuff in it */
127 auto overlay_qrcode = gtk_overlay_new();
128 gtk_box_pack_start(GTK_BOX(box_main), overlay_qrcode, FALSE, TRUE, 0);
129
130 /* box which will be in the overaly so that we can display the QR code over the stuff in this box */
131 priv->box_overlay = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);
132 gtk_container_add(GTK_CONTAINER(overlay_qrcode), priv->box_overlay);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400133
134 /* get logo */
135 GError *error = NULL;
136 GdkPixbuf* logo = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
137 350, -1, TRUE, &error);
138 if (logo == NULL) {
139 g_debug("Could not load logo: %s", error->message);
140 g_clear_error(&error);
141 } else {
142 auto image_ring_logo = gtk_image_new_from_pixbuf(logo);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400143 gtk_box_pack_start(GTK_BOX(priv->box_overlay), image_ring_logo, FALSE, TRUE, 0);
aviaudd355ba2016-04-08 14:23:09 -0400144 gtk_widget_set_visible(GTK_WIDGET(image_ring_logo), TRUE);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400145 }
146
147 /* welcome text */
Stepan Salenikovichd4d19d62016-11-03 11:12:22 -0400148 auto label_welcome_text = gtk_label_new(_("Ring is free software for universal communication which respects the freedoms and privacy of its users."));
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400149 gtk_label_set_justify(GTK_LABEL(label_welcome_text), GTK_JUSTIFY_CENTER);
Houmin68293822017-05-16 12:13:55 +0800150 PangoAttrList *attrs_welcome_text = pango_attr_list_new();
151 PangoAttribute *font_desc_welcome_text = pango_attr_font_desc_new(pango_font_description_from_string("12"));
152 pango_attr_list_insert(attrs_welcome_text, font_desc_welcome_text);
153 gtk_label_set_attributes(GTK_LABEL(label_welcome_text), attrs_welcome_text);
154 pango_attr_list_unref(attrs_welcome_text);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400155 gtk_label_set_line_wrap(GTK_LABEL(label_welcome_text), TRUE);
156 /* the max width chars is to limit how much the text expands */
157 gtk_label_set_max_width_chars(GTK_LABEL(label_welcome_text), 50);
158 gtk_label_set_selectable(GTK_LABEL(label_welcome_text), TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400159 gtk_box_pack_start(GTK_BOX(priv->box_overlay), label_welcome_text, FALSE, TRUE, 0);
aviaudd355ba2016-04-08 14:23:09 -0400160 gtk_widget_set_visible(GTK_WIDGET(label_welcome_text), TRUE);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400161
162 /* RingID explanation */
aviauf74c7b52016-12-01 13:57:00 -0500163 priv->label_explanation = gtk_label_new(NULL);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400164 auto context = gtk_widget_get_style_context(priv->label_explanation);
Stepan Salenikovichcebc5922015-10-22 15:43:09 -0400165 gtk_style_context_add_class(context, GTK_STYLE_CLASS_DIM_LABEL);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400166 gtk_label_set_justify(GTK_LABEL(priv->label_explanation), GTK_JUSTIFY_CENTER);
167 gtk_label_set_selectable(GTK_LABEL(priv->label_explanation), TRUE);
168 gtk_widget_set_margin_top(priv->label_explanation, 20);
169 gtk_widget_set_no_show_all(priv->label_explanation, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400170 gtk_box_pack_start(GTK_BOX(priv->box_overlay), priv->label_explanation, FALSE, TRUE, 0);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400171
172 /* RingID label */
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400173 priv->label_ringid = gtk_label_new(NULL);
174 gtk_label_set_selectable(GTK_LABEL(priv->label_ringid), TRUE);
Houmin68293822017-05-16 12:13:55 +0800175 PangoAttrList *attrs_ringid = pango_attr_list_new();
176 PangoAttribute *font_desc_ringid = pango_attr_font_desc_new(pango_font_description_from_string("monospace 12"));
177 pango_attr_list_insert(attrs_ringid, font_desc_ringid);
178 gtk_label_set_attributes(GTK_LABEL(priv->label_ringid), attrs_ringid);
179 pango_attr_list_unref(attrs_ringid);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400180 gtk_widget_set_no_show_all(priv->label_ringid, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400181 gtk_box_pack_start(GTK_BOX(box_main), priv->label_ringid, FALSE, TRUE, 0);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400182 gtk_label_set_ellipsize(GTK_LABEL(priv->label_ringid), PANGO_ELLIPSIZE_END);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400183
aviaudd355ba2016-04-08 14:23:09 -0400184 /* QR drawing area */
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400185 auto drawingarea_qrcode = gtk_drawing_area_new();
aviaudd355ba2016-04-08 14:23:09 -0400186 auto qrsize = 200;
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400187 gtk_widget_set_size_request(drawingarea_qrcode, qrsize, qrsize);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400188 g_signal_connect(drawingarea_qrcode, "draw", G_CALLBACK(draw_qrcode), self);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400189 gtk_widget_set_visible(drawingarea_qrcode, TRUE);
190
191 /* revealer which will show the qr code */
192 priv->revealer_qrcode = gtk_revealer_new();
193 gtk_container_add(GTK_CONTAINER(priv->revealer_qrcode), drawingarea_qrcode);
194 gtk_revealer_set_transition_type(GTK_REVEALER(priv->revealer_qrcode), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE); //GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
195 gtk_widget_set_halign(priv->revealer_qrcode, GTK_ALIGN_CENTER);
196 gtk_widget_set_valign(priv->revealer_qrcode, GTK_ALIGN_CENTER);
197 gtk_overlay_add_overlay(GTK_OVERLAY(overlay_qrcode), priv->revealer_qrcode);
198 gtk_widget_set_no_show_all(priv->revealer_qrcode, TRUE);
199 gtk_widget_set_visible(priv->revealer_qrcode, FALSE);
aviaudd355ba2016-04-08 14:23:09 -0400200
201 /* QR code button */
aviauf74c7b52016-12-01 13:57:00 -0500202 priv->button_qrcode = gtk_button_new_with_label(C_("Do not translate \"Ring ID\"", "Ring ID QR code"));
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400203 gtk_widget_set_hexpand(priv->button_qrcode, FALSE);
204 gtk_widget_set_size_request(priv->button_qrcode,10,10);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400205 g_signal_connect_swapped(priv->button_qrcode, "clicked", G_CALLBACK(switch_qrcode), self);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400206 gtk_widget_set_no_show_all(priv->button_qrcode, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400207 gtk_box_pack_start(GTK_BOX(box_main), priv->button_qrcode, TRUE, TRUE, 0);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400208
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400209 gtk_widget_show_all(GTK_WIDGET(self));
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400210}
211
212static void
213ring_welcome_view_dispose(GObject *object)
214{
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400215 G_OBJECT_CLASS(ring_welcome_view_parent_class)->dispose(object);
216}
217
218static void
219ring_welcome_view_finalize(GObject *object)
220{
221 G_OBJECT_CLASS(ring_welcome_view_parent_class)->finalize(object);
222}
223
224static void
225ring_welcome_view_class_init(RingWelcomeViewClass *klass)
226{
227 G_OBJECT_CLASS(klass)->finalize = ring_welcome_view_finalize;
228 G_OBJECT_CLASS(klass)->dispose = ring_welcome_view_dispose;
229}
230
231GtkWidget *
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400232ring_welcome_view_new(AccountInfoPointer const & accountInfo)
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400233{
234 gpointer self = g_object_new(RING_WELCOME_VIEW_TYPE, NULL);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400235 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400236 priv->accountInfo_ = &accountInfo;
237 ring_welcome_update_view(RING_WELCOME_VIEW(self));
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400238
239 return (GtkWidget *)self;
240}
aviaudd355ba2016-04-08 14:23:09 -0400241
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400242
aviaudd355ba2016-04-08 14:23:09 -0400243static gboolean
Stepan Salenikovich635258c2016-05-17 14:41:20 -0400244draw_qrcode(G_GNUC_UNUSED GtkWidget* diese,
aviaudd355ba2016-04-08 14:23:09 -0400245 cairo_t* cr,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400246 RingWelcomeView* self)
aviaudd355ba2016-04-08 14:23:09 -0400247{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400248 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400249
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400250 auto rcode = QRcode_encodeString((*priv->accountInfo_)->profileInfo.uri.c_str(),
aviaudd355ba2016-04-08 14:23:09 -0400251 0, //Let the version be decided by libqrencode
252 QR_ECLEVEL_L, // Lowest level of error correction
253 QR_MODE_8, // 8-bit data mode
254 1);
255
256 if (!rcode) { // no rcode, no draw
257 g_warning("Failed to generate QR code");
258 return TRUE;
259 }
260
261 auto margin = 5;
262 auto qrsize = 200;
263 int qrwidth = rcode->width + margin * 2;
264
265 /* scaling */
266 auto scale = qrsize/qrwidth;
267 cairo_scale(cr, scale, scale);
268
269 /* fill the background in white */
270 cairo_set_source_rgb(cr, 1, 1, 1);
271 cairo_rectangle(cr, 0, 0, qrwidth, qrwidth);
272 cairo_fill (cr);
273
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400274 unsigned char *p;
aviaudd355ba2016-04-08 14:23:09 -0400275 p = rcode->data;
276 cairo_set_source_rgb(cr, 0, 0, 0); // back in black
277 for(int y = 0; y < rcode->width; y++) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400278 unsigned char* row = (p + (y * rcode->width));
aviaudd355ba2016-04-08 14:23:09 -0400279 for(int x = 0; x < rcode->width; x++) {
280 if(*(row + x) & 0x1) {
281 cairo_rectangle(cr, margin + x, margin + y, 1, 1);
282 cairo_fill(cr);
283 }
284 }
aviaudd355ba2016-04-08 14:23:09 -0400285 }
286
287 QRcode_free(rcode);
288 return TRUE;
289
290}
291
292static void
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400293switch_qrcode(RingWelcomeView* self)
aviaudd355ba2016-04-08 14:23:09 -0400294{
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400295 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
296
297 auto to_reveal = !gtk_revealer_get_reveal_child(GTK_REVEALER(priv->revealer_qrcode));
298 gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer_qrcode), to_reveal);
299 gtk_widget_set_opacity(priv->box_overlay, to_reveal ? 0.25 : 1.0);
aviaudd355ba2016-04-08 14:23:09 -0400300}