summaryrefslogtreecommitdiff
path: root/src/contacts-qr-code-dialog.vala
blob: aca6c2b7f0cbc09a713f66c6b2662be83ef304f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
 * Copyright (C) 2018 Elias Entrup <elias-git@flump.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using Folks;
using GLib;

[GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-qr-code-dialog.ui")]
public class Contacts.QrCodeDialog : Gtk.Window {

  [GtkChild]
  private unowned Gtk.Picture qr_image;

  [GtkChild]
  private unowned Gtk.Label qr_subtitle;

  public QrCodeDialog.for_contact (Individual individual, Gtk.Window? parent = null) {
    Object (transient_for: parent);

    var subtitle = GLib.Markup.printf_escaped (_("Scan the QR code to save the contact <b>%s</b>."),
                                               individual.display_name);
    this.qr_subtitle.set_markup (subtitle);

    var individuals = new Gee.ArrayList<Individual> ();
    individuals.add (individual);

    var stringstream = new GLib.MemoryOutputStream.resizable ();
    var op = new Io.VCardExportOperation (individuals, stringstream);
    op.execute.begin ((obj, res) => {
      try {
        op.execute.end (res);
        uint8[] chars = {0};
        stringstream.write (chars);
        stringstream.close ();
      } catch (Error e) {
        warning ("ERROR: %s", e.message);
      }

      var content = (string) stringstream.steal_data ();
      int QR_IMAGE_SIZE = 300;
      var scale = this.qr_image.get_scale_factor ();
      create_qr_code (content, QR_IMAGE_SIZE * scale);
    });
  }

  private void create_qr_code (string content, int size) {
    if (content == "") {
      warning ("Failed to create QR code: no content");
      return;
    }

    var result = new QRencode.QRcode.encodeString (content,
                                                   0,
                                                   QRencode.EcLevel.M,
                                                   QRencode.Mode.B8,
                                                   1);
    if (result == null) {
      warning ("Failed to create QR code: libqrencode error");
      return;
    }

    var qr_size = result.width;
    var pixel_size = (int) double.max (1, size / qr_size);
    var total_size = qr_size * pixel_size;
    var BYTES_PER_R8G8B8 = 3;
    var qr_matrix = new GLib.ByteArray.sized ((uint)(total_size * total_size * pixel_size * BYTES_PER_R8G8B8));

    for (var column = 0; column < total_size; column++) {
      for (var i = 0; i < pixel_size; i++) {
        for (var row = 0; row < total_size / pixel_size; row++) {
          if ((result.data[qr_size*row + column] & 0x01) > 0) {
            fill_pixel (qr_matrix, 0x00, pixel_size);
          } else {
            fill_pixel (qr_matrix, 0xff, pixel_size);
          }
        }
      }
    }

    var bytes = ByteArray.free_to_bytes (qr_matrix);
    var paintable = new Gdk.MemoryTexture (total_size, total_size,
                                           Gdk.MemoryFormat.R8G8B8,
                                           bytes,
                                           total_size * BYTES_PER_R8G8B8);
    this.qr_image.set_paintable (paintable);
  }

  private void fill_pixel (GLib.ByteArray array, uint8 val, int pixel_size) {
    for (uint i = 0; i < pixel_size; i++) {
      array.append ({val}); // R
      array.append ({val}); // G
      array.append ({val}); // B
    }
  }
}