blob: ca353ebfc03bc9913dac9fbc655f87caacef894d [file] [log] [blame]
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04001/*
Guillaume Roguez2a6150d2017-07-19 18:24:47 -04002 * Copyright (C) 2015-2017 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
Sébastien Blin55bff9d2017-10-03 15:15:23 -040055 AccountContainer* accountContainer_;
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
66ring_welcome_update_view(RingWelcomeView* self, AccountContainer* accountContainer) {
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 if (accountContainer)
70 priv->accountContainer_ = accountContainer;
Nicolas Jager15a8b902017-03-21 07:53:06 -040071
Sébastien Blin55bff9d2017-10-03 15:15:23 -040072 // Only draw a basic view for SIP accounts
73 if (not accountContainer || priv->accountContainer_->info.profileInfo.type == lrc::api::profile::Type::SIP) {
Nicolas Jager15a8b902017-03-21 07:53:06 -040074 gtk_widget_hide(priv->button_qrcode);
Sébastien Blin55bff9d2017-10-03 15:15:23 -040075 gtk_widget_hide(priv->label_ringid);
76 gtk_widget_hide(priv->label_explanation);
Nicolas Jager15a8b902017-03-21 07:53:06 -040077 gtk_widget_hide(priv->revealer_qrcode);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040078 gtk_widget_set_opacity(priv->box_overlay, 1.0);
Sébastien Blin55bff9d2017-10-03 15:15:23 -040079 gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer_qrcode), FALSE);
80 return;
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040081 }
Sébastien Blin55bff9d2017-10-03 15:15:23 -040082
83 // Get registeredName, else the Ring Id
84 gchar *ring_id = nullptr;
85 if(! priv->accountContainer_->info.registeredName.empty()){
86 gtk_label_set_text(
87 GTK_LABEL(priv->label_explanation),
88 _("This is your Ring username.\nCopy and share it with your friends!")
89 );
90 ring_id = g_markup_printf_escaped("<span fgcolor=\"black\">ring:%s</span>",
91 priv->accountContainer_->info.registeredName.c_str());
92 }
93 else if (!priv->accountContainer_->info.profileInfo.uri.empty()) {
94 gtk_label_set_text(
95 GTK_LABEL(priv->label_explanation),
96 C_("Do not translate \"RingID\"", "This is your RingID.\nCopy and share it with your friends!")
97 );
98 ring_id = g_markup_printf_escaped("<span fgcolor=\"black\">%s</span>",
99 priv->accountContainer_->info.profileInfo.uri.c_str());
100 } else {
101 gtk_label_set_text(GTK_LABEL(priv->label_explanation), NULL);
102 ring_id[0] = '\0';
103 }
104
105 gtk_label_set_markup(GTK_LABEL(priv->label_ringid), ring_id);
106
107 gtk_widget_show(priv->label_explanation);
108 gtk_widget_show(priv->label_ringid);
109 gtk_widget_show(priv->button_qrcode);
110 gtk_widget_show(priv->revealer_qrcode);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400111}
112
113static void
114ring_welcome_view_init(RingWelcomeView *self)
115{
116 RingWelcomeViewPrivate *priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
117
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -0500118 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
119
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400120 auto box_main = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);
121 gtk_container_add(GTK_CONTAINER(self), box_main);
122 gtk_box_set_baseline_position(GTK_BOX(box_main), GTK_BASELINE_POSITION_CENTER);
123 gtk_widget_set_vexpand(GTK_WIDGET(box_main), TRUE);
124 gtk_widget_set_hexpand(GTK_WIDGET(box_main), FALSE);
125 gtk_widget_set_valign(GTK_WIDGET(box_main), GTK_ALIGN_CENTER);
126 gtk_widget_set_halign(GTK_WIDGET(box_main), GTK_ALIGN_CENTER);
127 gtk_widget_set_visible(GTK_WIDGET(box_main), TRUE);
128
129 /* overlay to show hide the qr code over the stuff in it */
130 auto overlay_qrcode = gtk_overlay_new();
131 gtk_box_pack_start(GTK_BOX(box_main), overlay_qrcode, FALSE, TRUE, 0);
132
133 /* box which will be in the overaly so that we can display the QR code over the stuff in this box */
134 priv->box_overlay = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);
135 gtk_container_add(GTK_CONTAINER(overlay_qrcode), priv->box_overlay);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400136
137 /* get logo */
138 GError *error = NULL;
139 GdkPixbuf* logo = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
140 350, -1, TRUE, &error);
141 if (logo == NULL) {
142 g_debug("Could not load logo: %s", error->message);
143 g_clear_error(&error);
144 } else {
145 auto image_ring_logo = gtk_image_new_from_pixbuf(logo);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400146 gtk_box_pack_start(GTK_BOX(priv->box_overlay), image_ring_logo, FALSE, TRUE, 0);
aviaudd355ba2016-04-08 14:23:09 -0400147 gtk_widget_set_visible(GTK_WIDGET(image_ring_logo), TRUE);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400148 }
149
150 /* welcome text */
Stepan Salenikovichd4d19d62016-11-03 11:12:22 -0400151 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 -0400152 gtk_label_set_justify(GTK_LABEL(label_welcome_text), GTK_JUSTIFY_CENTER);
Houmin68293822017-05-16 12:13:55 +0800153 PangoAttrList *attrs_welcome_text = pango_attr_list_new();
154 PangoAttribute *font_desc_welcome_text = pango_attr_font_desc_new(pango_font_description_from_string("12"));
155 pango_attr_list_insert(attrs_welcome_text, font_desc_welcome_text);
156 gtk_label_set_attributes(GTK_LABEL(label_welcome_text), attrs_welcome_text);
157 pango_attr_list_unref(attrs_welcome_text);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400158 gtk_label_set_line_wrap(GTK_LABEL(label_welcome_text), TRUE);
159 /* the max width chars is to limit how much the text expands */
160 gtk_label_set_max_width_chars(GTK_LABEL(label_welcome_text), 50);
161 gtk_label_set_selectable(GTK_LABEL(label_welcome_text), TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400162 gtk_box_pack_start(GTK_BOX(priv->box_overlay), label_welcome_text, FALSE, TRUE, 0);
aviaudd355ba2016-04-08 14:23:09 -0400163 gtk_widget_set_visible(GTK_WIDGET(label_welcome_text), TRUE);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400164
165 /* RingID explanation */
aviauf74c7b52016-12-01 13:57:00 -0500166 priv->label_explanation = gtk_label_new(NULL);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400167 auto context = gtk_widget_get_style_context(priv->label_explanation);
Stepan Salenikovichcebc5922015-10-22 15:43:09 -0400168 gtk_style_context_add_class(context, GTK_STYLE_CLASS_DIM_LABEL);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400169 gtk_label_set_justify(GTK_LABEL(priv->label_explanation), GTK_JUSTIFY_CENTER);
170 gtk_label_set_selectable(GTK_LABEL(priv->label_explanation), TRUE);
171 gtk_widget_set_margin_top(priv->label_explanation, 20);
172 gtk_widget_set_no_show_all(priv->label_explanation, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400173 gtk_box_pack_start(GTK_BOX(priv->box_overlay), priv->label_explanation, FALSE, TRUE, 0);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400174
175 /* RingID label */
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400176 priv->label_ringid = gtk_label_new(NULL);
177 gtk_label_set_selectable(GTK_LABEL(priv->label_ringid), TRUE);
Houmin68293822017-05-16 12:13:55 +0800178 PangoAttrList *attrs_ringid = pango_attr_list_new();
179 PangoAttribute *font_desc_ringid = pango_attr_font_desc_new(pango_font_description_from_string("monospace 12"));
180 pango_attr_list_insert(attrs_ringid, font_desc_ringid);
181 gtk_label_set_attributes(GTK_LABEL(priv->label_ringid), attrs_ringid);
182 pango_attr_list_unref(attrs_ringid);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400183 gtk_widget_set_no_show_all(priv->label_ringid, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400184 gtk_box_pack_start(GTK_BOX(box_main), priv->label_ringid, FALSE, TRUE, 0);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400185 gtk_label_set_ellipsize(GTK_LABEL(priv->label_ringid), PANGO_ELLIPSIZE_END);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400186
aviaudd355ba2016-04-08 14:23:09 -0400187 /* QR drawing area */
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400188 auto drawingarea_qrcode = gtk_drawing_area_new();
aviaudd355ba2016-04-08 14:23:09 -0400189 auto qrsize = 200;
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400190 gtk_widget_set_size_request(drawingarea_qrcode, qrsize, qrsize);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400191 g_signal_connect(drawingarea_qrcode, "draw", G_CALLBACK(draw_qrcode), self);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400192 gtk_widget_set_visible(drawingarea_qrcode, TRUE);
193
194 /* revealer which will show the qr code */
195 priv->revealer_qrcode = gtk_revealer_new();
196 gtk_container_add(GTK_CONTAINER(priv->revealer_qrcode), drawingarea_qrcode);
197 gtk_revealer_set_transition_type(GTK_REVEALER(priv->revealer_qrcode), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE); //GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
198 gtk_widget_set_halign(priv->revealer_qrcode, GTK_ALIGN_CENTER);
199 gtk_widget_set_valign(priv->revealer_qrcode, GTK_ALIGN_CENTER);
200 gtk_overlay_add_overlay(GTK_OVERLAY(overlay_qrcode), priv->revealer_qrcode);
201 gtk_widget_set_no_show_all(priv->revealer_qrcode, TRUE);
202 gtk_widget_set_visible(priv->revealer_qrcode, FALSE);
aviaudd355ba2016-04-08 14:23:09 -0400203
204 /* QR code button */
aviauf74c7b52016-12-01 13:57:00 -0500205 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 -0400206 gtk_widget_set_hexpand(priv->button_qrcode, FALSE);
207 gtk_widget_set_size_request(priv->button_qrcode,10,10);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400208 g_signal_connect_swapped(priv->button_qrcode, "clicked", G_CALLBACK(switch_qrcode), self);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400209 gtk_widget_set_no_show_all(priv->button_qrcode, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400210 gtk_box_pack_start(GTK_BOX(box_main), priv->button_qrcode, TRUE, TRUE, 0);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400211
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400212 gtk_widget_show_all(GTK_WIDGET(self));
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400213}
214
215static void
216ring_welcome_view_dispose(GObject *object)
217{
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400218 G_OBJECT_CLASS(ring_welcome_view_parent_class)->dispose(object);
219}
220
221static void
222ring_welcome_view_finalize(GObject *object)
223{
224 G_OBJECT_CLASS(ring_welcome_view_parent_class)->finalize(object);
225}
226
227static void
228ring_welcome_view_class_init(RingWelcomeViewClass *klass)
229{
230 G_OBJECT_CLASS(klass)->finalize = ring_welcome_view_finalize;
231 G_OBJECT_CLASS(klass)->dispose = ring_welcome_view_dispose;
232}
233
234GtkWidget *
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400235ring_welcome_view_new(AccountContainer* accountContainer)
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400236{
237 gpointer self = g_object_new(RING_WELCOME_VIEW_TYPE, NULL);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400238 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
239 priv->accountContainer_ = accountContainer;
240 ring_welcome_update_view(RING_WELCOME_VIEW(self), accountContainer);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400241
242 return (GtkWidget *)self;
243}
aviaudd355ba2016-04-08 14:23:09 -0400244
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400245
aviaudd355ba2016-04-08 14:23:09 -0400246static gboolean
Stepan Salenikovich635258c2016-05-17 14:41:20 -0400247draw_qrcode(G_GNUC_UNUSED GtkWidget* diese,
aviaudd355ba2016-04-08 14:23:09 -0400248 cairo_t* cr,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400249 RingWelcomeView* self)
aviaudd355ba2016-04-08 14:23:09 -0400250{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400251 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400252
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400253 auto rcode = QRcode_encodeString(priv->accountContainer_->info.profileInfo.uri.c_str(),
aviaudd355ba2016-04-08 14:23:09 -0400254 0, //Let the version be decided by libqrencode
255 QR_ECLEVEL_L, // Lowest level of error correction
256 QR_MODE_8, // 8-bit data mode
257 1);
258
259 if (!rcode) { // no rcode, no draw
260 g_warning("Failed to generate QR code");
261 return TRUE;
262 }
263
264 auto margin = 5;
265 auto qrsize = 200;
266 int qrwidth = rcode->width + margin * 2;
267
268 /* scaling */
269 auto scale = qrsize/qrwidth;
270 cairo_scale(cr, scale, scale);
271
272 /* fill the background in white */
273 cairo_set_source_rgb(cr, 1, 1, 1);
274 cairo_rectangle(cr, 0, 0, qrwidth, qrwidth);
275 cairo_fill (cr);
276
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400277 unsigned char *p;
aviaudd355ba2016-04-08 14:23:09 -0400278 p = rcode->data;
279 cairo_set_source_rgb(cr, 0, 0, 0); // back in black
280 for(int y = 0; y < rcode->width; y++) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400281 unsigned char* row = (p + (y * rcode->width));
aviaudd355ba2016-04-08 14:23:09 -0400282 for(int x = 0; x < rcode->width; x++) {
283 if(*(row + x) & 0x1) {
284 cairo_rectangle(cr, margin + x, margin + y, 1, 1);
285 cairo_fill(cr);
286 }
287 }
aviaudd355ba2016-04-08 14:23:09 -0400288 }
289
290 QRcode_free(rcode);
291 return TRUE;
292
293}
294
295static void
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400296switch_qrcode(RingWelcomeView* self)
aviaudd355ba2016-04-08 14:23:09 -0400297{
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400298 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
299
300 auto to_reveal = !gtk_revealer_get_reveal_child(GTK_REVEALER(priv->revealer_qrcode));
301 gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer_qrcode), to_reveal);
302 gtk_widget_set_opacity(priv->box_overlay, to_reveal ? 0.25 : 1.0);
aviaudd355ba2016-04-08 14:23:09 -0400303}