blob: 83b4ef60223e927bb3e5717b836adfcc25eea933 [file] [log] [blame]
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovich2cde7612015-09-25 10:44:01 -04003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "ringwelcomeview.h"
21
22#include <gtk/gtk.h>
23#include <glib/gi18n.h>
24#include "utils/accounts.h"
25#include <account.h>
26#include <accountmodel.h>
aviaudd355ba2016-04-08 14:23:09 -040027#include <qrencode.h>
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040028
29struct _RingWelcomeView
30{
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050031 GtkScrolledWindow parent;
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040032};
33
34struct _RingWelcomeViewClass
35{
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050036 GtkScrolledWindowClass parent_class;
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040037};
38
39typedef struct _RingWelcomeViewPrivate RingWelcomeViewPrivate;
40
41struct _RingWelcomeViewPrivate
42{
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040043 GtkWidget *box_overlay;
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040044 GtkWidget *label_ringid;
45 GtkWidget *label_explanation;
46 GtkWidget *button_qrcode;
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040047 GtkWidget *revealer_qrcode;
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040048
aviaudf1f97b2016-10-24 12:34:14 -040049 QMetaObject::Connection account_model_data_changed;
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040050};
51
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050052G_DEFINE_TYPE_WITH_PRIVATE(RingWelcomeView, ring_welcome_view, GTK_TYPE_SCROLLED_WINDOW);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040053
54#define RING_WELCOME_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_WELCOME_VIEW_TYPE, RingWelcomeViewPrivate))
55
aviaudd355ba2016-04-08 14:23:09 -040056static gboolean draw_qrcode(GtkWidget*,cairo_t*,gpointer);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040057static void switch_qrcode(RingWelcomeView* self);
aviaudd355ba2016-04-08 14:23:09 -040058
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040059static void
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040060update_view(RingWelcomeView *self) {
61 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040062
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040063 auto account = get_active_ring_account();
64 if (account != nullptr) {
65 gchar *ring_id = nullptr;
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040066 if (!account->username().isEmpty()) {
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040067 ring_id = g_markup_printf_escaped("<span fgcolor=\"black\">%s</span>",
68 account->username().toUtf8().constData());
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040069 } else {
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040070 ring_id = g_markup_printf_escaped("<span fgcolor=\"gray\">%s</span>",
71 _("fetching RingID..."));
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040072 }
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040073 gtk_label_set_markup(GTK_LABEL(priv->label_ringid), ring_id);
74 g_free(ring_id);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040075 }
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040076
77 gtk_widget_set_visible(priv->label_ringid, account != nullptr);
78 gtk_widget_set_visible(priv->label_explanation, account != nullptr);
79 gtk_widget_set_visible(priv->button_qrcode, account != nullptr);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040080 gtk_widget_set_visible(priv->revealer_qrcode, account != nullptr);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -040081
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040082 if (!account) {
83 gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer_qrcode), FALSE);
84 gtk_widget_set_opacity(priv->box_overlay, 1.0);
85 }
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040086}
87
88static void
89ring_welcome_view_init(RingWelcomeView *self)
90{
91 RingWelcomeViewPrivate *priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
92
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050093 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
94
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -040095 auto box_main = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);
96 gtk_container_add(GTK_CONTAINER(self), box_main);
97 gtk_box_set_baseline_position(GTK_BOX(box_main), GTK_BASELINE_POSITION_CENTER);
98 gtk_widget_set_vexpand(GTK_WIDGET(box_main), TRUE);
99 gtk_widget_set_hexpand(GTK_WIDGET(box_main), FALSE);
100 gtk_widget_set_valign(GTK_WIDGET(box_main), GTK_ALIGN_CENTER);
101 gtk_widget_set_halign(GTK_WIDGET(box_main), GTK_ALIGN_CENTER);
102 gtk_widget_set_visible(GTK_WIDGET(box_main), TRUE);
103
104 /* overlay to show hide the qr code over the stuff in it */
105 auto overlay_qrcode = gtk_overlay_new();
106 gtk_box_pack_start(GTK_BOX(box_main), overlay_qrcode, FALSE, TRUE, 0);
107
108 /* box which will be in the overaly so that we can display the QR code over the stuff in this box */
109 priv->box_overlay = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);
110 gtk_container_add(GTK_CONTAINER(overlay_qrcode), priv->box_overlay);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400111
112 /* get logo */
113 GError *error = NULL;
114 GdkPixbuf* logo = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
115 350, -1, TRUE, &error);
116 if (logo == NULL) {
117 g_debug("Could not load logo: %s", error->message);
118 g_clear_error(&error);
119 } else {
120 auto image_ring_logo = gtk_image_new_from_pixbuf(logo);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400121 gtk_box_pack_start(GTK_BOX(priv->box_overlay), image_ring_logo, FALSE, TRUE, 0);
aviaudd355ba2016-04-08 14:23:09 -0400122 gtk_widget_set_visible(GTK_WIDGET(image_ring_logo), TRUE);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400123 }
124
125 /* welcome text */
Stepan Salenikovichd4d19d62016-11-03 11:12:22 -0400126 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 -0400127 gtk_label_set_justify(GTK_LABEL(label_welcome_text), GTK_JUSTIFY_CENTER);
128 gtk_widget_override_font(label_welcome_text, pango_font_description_from_string("12"));
129 gtk_label_set_line_wrap(GTK_LABEL(label_welcome_text), TRUE);
130 /* the max width chars is to limit how much the text expands */
131 gtk_label_set_max_width_chars(GTK_LABEL(label_welcome_text), 50);
132 gtk_label_set_selectable(GTK_LABEL(label_welcome_text), TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400133 gtk_box_pack_start(GTK_BOX(priv->box_overlay), label_welcome_text, FALSE, TRUE, 0);
aviaudd355ba2016-04-08 14:23:09 -0400134 gtk_widget_set_visible(GTK_WIDGET(label_welcome_text), TRUE);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400135
136 /* RingID explanation */
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400137 priv->label_explanation = gtk_label_new(C_("Do not translate \"RingID\"", "This is your RingID.\nCopy and share it with your friends!"));
138 auto context = gtk_widget_get_style_context(priv->label_explanation);
Stepan Salenikovichcebc5922015-10-22 15:43:09 -0400139 gtk_style_context_add_class(context, GTK_STYLE_CLASS_DIM_LABEL);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400140 gtk_label_set_justify(GTK_LABEL(priv->label_explanation), GTK_JUSTIFY_CENTER);
141 gtk_label_set_selectable(GTK_LABEL(priv->label_explanation), TRUE);
142 gtk_widget_set_margin_top(priv->label_explanation, 20);
143 gtk_widget_set_no_show_all(priv->label_explanation, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400144 gtk_box_pack_start(GTK_BOX(priv->box_overlay), priv->label_explanation, FALSE, TRUE, 0);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400145
146 /* RingID label */
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400147 priv->label_ringid = gtk_label_new(NULL);
148 gtk_label_set_selectable(GTK_LABEL(priv->label_ringid), TRUE);
149 gtk_widget_override_font(priv->label_ringid, pango_font_description_from_string("monospace 12"));
150 gtk_widget_set_no_show_all(priv->label_ringid, TRUE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400151 gtk_box_pack_start(GTK_BOX(box_main), priv->label_ringid, FALSE, TRUE, 0);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400152 gtk_label_set_ellipsize(GTK_LABEL(priv->label_ringid), PANGO_ELLIPSIZE_END);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400153
aviaudd355ba2016-04-08 14:23:09 -0400154 /* QR drawing area */
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400155 auto drawingarea_qrcode = gtk_drawing_area_new();
aviaudd355ba2016-04-08 14:23:09 -0400156 auto qrsize = 200;
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400157 gtk_widget_set_size_request(drawingarea_qrcode, qrsize, qrsize);
158 g_signal_connect(drawingarea_qrcode, "draw", G_CALLBACK(draw_qrcode), NULL);
159 gtk_widget_set_visible(drawingarea_qrcode, TRUE);
160
161 /* revealer which will show the qr code */
162 priv->revealer_qrcode = gtk_revealer_new();
163 gtk_container_add(GTK_CONTAINER(priv->revealer_qrcode), drawingarea_qrcode);
164 gtk_revealer_set_transition_type(GTK_REVEALER(priv->revealer_qrcode), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE); //GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
165 gtk_widget_set_halign(priv->revealer_qrcode, GTK_ALIGN_CENTER);
166 gtk_widget_set_valign(priv->revealer_qrcode, GTK_ALIGN_CENTER);
167 gtk_overlay_add_overlay(GTK_OVERLAY(overlay_qrcode), priv->revealer_qrcode);
168 gtk_widget_set_no_show_all(priv->revealer_qrcode, TRUE);
169 gtk_widget_set_visible(priv->revealer_qrcode, FALSE);
aviaudd355ba2016-04-08 14:23:09 -0400170
171 /* QR code button */
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400172 priv->button_qrcode = gtk_button_new_with_label("QR code");
173 gtk_widget_set_hexpand(priv->button_qrcode, FALSE);
174 gtk_widget_set_size_request(priv->button_qrcode,10,10);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400175 g_signal_connect_swapped(priv->button_qrcode, "clicked", G_CALLBACK(switch_qrcode), self);
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400176 gtk_widget_set_no_show_all(priv->button_qrcode, TRUE);
177 gtk_widget_set_visible(priv->button_qrcode, FALSE);
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400178 gtk_box_pack_start(GTK_BOX(box_main), priv->button_qrcode, TRUE, TRUE, 0);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400179
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400180 update_view(self);
181
aviaudf1f97b2016-10-24 12:34:14 -0400182 priv->account_model_data_changed = QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400183 &AccountModel::instance(),
aviaudf1f97b2016-10-24 12:34:14 -0400184 &AccountModel::dataChanged,
185 [self] (const QModelIndex&, const QModelIndex&) { update_view(self); }
Stepan Salenikovich64d446c2016-09-15 10:56:55 -0400186 );
187
188 gtk_widget_show_all(GTK_WIDGET(self));
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400189}
190
191static void
192ring_welcome_view_dispose(GObject *object)
193{
194 RingWelcomeView *self = RING_WELCOME_VIEW(object);
195 RingWelcomeViewPrivate *priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
196
aviaudf1f97b2016-10-24 12:34:14 -0400197 QObject::disconnect(priv->account_model_data_changed);
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400198
199 G_OBJECT_CLASS(ring_welcome_view_parent_class)->dispose(object);
200}
201
202static void
203ring_welcome_view_finalize(GObject *object)
204{
205 G_OBJECT_CLASS(ring_welcome_view_parent_class)->finalize(object);
206}
207
208static void
209ring_welcome_view_class_init(RingWelcomeViewClass *klass)
210{
211 G_OBJECT_CLASS(klass)->finalize = ring_welcome_view_finalize;
212 G_OBJECT_CLASS(klass)->dispose = ring_welcome_view_dispose;
213}
214
215GtkWidget *
216ring_welcome_view_new()
217{
218 gpointer self = g_object_new(RING_WELCOME_VIEW_TYPE, NULL);
219
220 return (GtkWidget *)self;
221}
aviaudd355ba2016-04-08 14:23:09 -0400222
223static gboolean
Stepan Salenikovich635258c2016-05-17 14:41:20 -0400224draw_qrcode(G_GNUC_UNUSED GtkWidget* diese,
aviaudd355ba2016-04-08 14:23:09 -0400225 cairo_t* cr,
Stepan Salenikovich635258c2016-05-17 14:41:20 -0400226 G_GNUC_UNUSED gpointer data)
aviaudd355ba2016-04-08 14:23:09 -0400227{
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400228 auto account = get_active_ring_account();
229 if (!account)
230 return TRUE;
231
232 auto rcode = QRcode_encodeString(account->username().toStdString().c_str(),
aviaudd355ba2016-04-08 14:23:09 -0400233 0, //Let the version be decided by libqrencode
234 QR_ECLEVEL_L, // Lowest level of error correction
235 QR_MODE_8, // 8-bit data mode
236 1);
237
238 if (!rcode) { // no rcode, no draw
239 g_warning("Failed to generate QR code");
240 return TRUE;
241 }
242
243 auto margin = 5;
244 auto qrsize = 200;
245 int qrwidth = rcode->width + margin * 2;
246
247 /* scaling */
248 auto scale = qrsize/qrwidth;
249 cairo_scale(cr, scale, scale);
250
251 /* fill the background in white */
252 cairo_set_source_rgb(cr, 1, 1, 1);
253 cairo_rectangle(cr, 0, 0, qrwidth, qrwidth);
254 cairo_fill (cr);
255
256 unsigned char *row, *p;
257 p = rcode->data;
258 cairo_set_source_rgb(cr, 0, 0, 0); // back in black
259 for(int y = 0; y < rcode->width; y++) {
260 row = (p + (y * rcode->width));
261 for(int x = 0; x < rcode->width; x++) {
262 if(*(row + x) & 0x1) {
263 cairo_rectangle(cr, margin + x, margin + y, 1, 1);
264 cairo_fill(cr);
265 }
266 }
267
268 }
269
270 QRcode_free(rcode);
271 return TRUE;
272
273}
274
275static void
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400276switch_qrcode(RingWelcomeView* self)
aviaudd355ba2016-04-08 14:23:09 -0400277{
Stepan Salenikovich3e2851a2016-09-15 12:08:13 -0400278 auto priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
279
280 auto to_reveal = !gtk_revealer_get_reveal_child(GTK_REVEALER(priv->revealer_qrcode));
281 gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer_qrcode), to_reveal);
282 gtk_widget_set_opacity(priv->box_overlay, to_reveal ? 0.25 : 1.0);
aviaudd355ba2016-04-08 14:23:09 -0400283}