gnucash maint: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Sat Dec 12 20:08:42 EST 2020


Updated	 via  https://github.com/Gnucash/gnucash/commit/2b01bde2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/580975b9 (commit)
	from  https://github.com/Gnucash/gnucash/commit/7de24dec (commit)



commit 2b01bde2b6808d09a6ca9ef676ca2384fb005bb8
Merge: 7de24dec6 580975b92
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 12 17:00:47 2020 -0800

    Merge Christian Wehling's 'gnucash-flicker' into maint.

diff --cc gnucash/import-export/aqb/gnc-gwen-gui.c
index 54fa72953,aeb70b481..1a0569938
--- a/gnucash/import-export/aqb/gnc-gwen-gui.c
+++ b/gnucash/import-export/aqb/gnc-gwen-gui.c
@@@ -949,9 -954,33 +954,46 @@@ get_input(GncGWENGui *gui, guint32 flag
      remember_pin_checkbutton = GTK_WIDGET(gtk_builder_get_object (builder, "remember_pin"));
      optical_challenge = GTK_IMAGE(gtk_builder_get_object (builder, "optical_challenge"));
      gtk_widget_set_visible(GTK_WIDGET(optical_challenge), FALSE);
+ 
+     flickergui = g_slice_new(GncFlickerGui);
+     flickergui->flicker_challenge = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_challenge"));
+     flickergui->flicker_marker = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_marker"));
+     flickergui->flicker_hbox = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_hbox"));
+     flickergui->spin_barwidth = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spin_barwidth"));
+     flickergui->spin_delay = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spin_delay"));
+ 
+     gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_challenge), FALSE);
+     gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_marker), FALSE);
+     gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_hbox), FALSE);
+     gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_barwidth), FALSE);
+     gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_delay), FALSE);
+ 
      #ifdef AQBANKING6
-     if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
+     if (g_strcmp0(mimeType,"text/x-flickercode") == 0 && pChallenge != NULL)
++<<<<<<< Updated upstream
      {
+         /* Chiptan Optic (aka Flicker) */
+         gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_challenge), TRUE);
+         gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_marker), TRUE);
+         gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_hbox), TRUE);
+         gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_barwidth), TRUE);
+         gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_delay), TRUE);
+     }
+     else if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
+     {
++=======
++    {
++        /* Chiptan Optic (aka Flicker) */
++        gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_challenge), TRUE);
++        gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_marker), TRUE);
++        gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_hbox), TRUE);
++        gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_barwidth), TRUE);
++        gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_delay), TRUE);
++    }
++    else if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
++    {
++>>>>>>> Stashed changes
+         /* Phototan or Chiptan QR */
          gtk_widget_set_visible(GTK_WIDGET(optical_challenge), TRUE);
      }
      #endif
@@@ -993,9 -1023,10 +1036,22 @@@
      }
  
      #ifdef AQBANKING6
--    //if (optical_challenge)
--    if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
++    /* Optical challenge. Flickercode sets the mimetype to
++     * x-flickercode and doesn't set the challenge length */
++    if (g_strcmp0(mimeType,"text/x-flickercode") == 0 && pChallenge != NULL)
 +    {
++         /* Chiptan Optic (aka Flicker) */
++         flickergui->dialog = dialog;
++         flickergui->input_entry = input_entry;
++
++         ini_flicker_gui(pChallenge, flickergui);
++         g_slice_free(GncFlickerGui, flickergui);
++    }
++    /* While phototan has multiple mimetypes and does set the
++     * challenge length. */
++    else if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
+     {
+         /* Phototan or Chiptan QR */
          // convert PNG and load into widget
          // TBD: check mimeType?
          guchar *gudata = (guchar*)pChallenge;
@@@ -1019,7 -1050,16 +1075,7 @@@
  
          gtk_image_set_from_pixbuf(optical_challenge, pixbuf);
      }
 -    else if (g_strcmp0(mimeType,"text/x-flickercode") == 0 && pChallenge != NULL)
 -    {
 -        /* Chiptan Optic (aka Flicker) */
 -        flickergui->dialog = dialog;
 -        flickergui->input_entry = input_entry;
 -
 -        ini_flicker_gui(pChallenge, flickergui);
 -        g_slice_free(GncFlickerGui, flickergui);
 -    }
--    #endif
++#endif
  
      if (*input)
      {
diff --cc po/POTFILES.in
index e1ffe403f,e1ffe403f..09c6ccc9b
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@@ -315,9 -315,9 +315,11 @@@ gnucash/import-export/aqb/gnc-ab-kvp.
  gnucash/import-export/aqb/gnc-ab-transfer.c
  gnucash/import-export/aqb/gnc-ab-utils.c
  gnucash/import-export/aqb/gnc-file-aqb-import.c
++gnucash/import-export/aqb/gnc-flicker-gui.c
  gnucash/import-export/aqb/gnc-gwen-gui.c
  gnucash/import-export/aqb/gncmod-aqbanking.c
  gnucash/import-export/aqb/gnc-plugin-aqbanking.c
++gnucash/import-export/aqb/gschemas/org.gnucash.dialogs.flicker.gschema.xml.in
  gnucash/import-export/aqb/gschemas/org.gnucash.dialogs.import.hbci.gschema.xml.in
  gnucash/import-export/bi-import/dialog-bi-import.c
  gnucash/import-export/bi-import/dialog-bi-import-gui.c

commit 580975b924fb5b3c7cdf9f2ad04cb27b09a51fa7
Author: CWehli <christian.wehling at web.de>
Date:   Sun Nov 15 15:06:09 2020 +0100

    Bug 667490 - Support chipTAN optical "Flicker code"
    
    Partly fixes bug 667490.
    It implements the display of flashing optical TAN challenges (aka
    flicker) in the "Enter TAN" dialog box.

diff --git a/gnucash/import-export/aqb/CMakeLists.txt b/gnucash/import-export/aqb/CMakeLists.txt
index e29c531d0..458b516e6 100644
--- a/gnucash/import-export/aqb/CMakeLists.txt
+++ b/gnucash/import-export/aqb/CMakeLists.txt
@@ -13,6 +13,7 @@ set (aqbanking_SOURCES
   gnc-ab-transfer.c
   gnc-ab-utils.c
   gnc-file-aqb-import.c
+  gnc-flicker-gui.c
   gnc-gwen-gui.c
   gnc-plugin-aqbanking.c
   gncmod-aqbanking.c
@@ -31,6 +32,7 @@ set (aqbanking_noinst_HEADERS
   gnc-ab-transfer.h
   gnc-ab-utils.h
   gnc-file-aqb-import.h
+  gnc-flicker-gui.h
   gnc-gwen-gui.h
   gnc-plugin-aqbanking.h
 )
diff --git a/gnucash/import-export/aqb/dialog-ab.glade b/gnucash/import-export/aqb/dialog-ab.glade
index d0e4450fe..0d78a5a83 100644
--- a/gnucash/import-export/aqb/dialog-ab.glade
+++ b/gnucash/import-export/aqb/dialog-ab.glade
@@ -634,14 +634,90 @@
               </packing>
             </child>
             <child>
-              <object class="GtkImage" id="optical_challenge">
-                <property name="name">optical_challenge</property>
+              <object class="GtkBox" id="flicker_hbox">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
+                <property name="spacing">7</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">end</property>
+                    <property name="label" translatable="yes">Bar_width</property>
+                    <property name="xalign">0.7</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">spin_barwidth</property>
+                    <attributes>
+                      <attribute name="gravity" value="west"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spin_barwidth">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip-text" translatable="yes" comments="TAN generator with flicker interface common in DE only">Setting the bar width, adapting to the size of the TAN generator.</property>
+                    <property name="numeric">True</property>
+                    <property name="update_policy">if-valid</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">end</property>
+                    <property name="label" translatable="yes">_Delay</property>
+                    <property name="xalign">0.7</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">spin_delay</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spin_delay">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip-text" translatable="yes" comments="TAN generator with flicker interface common in DE only">Setting the delay time, with small values the flicker graphic is repeated faster.</property>
+                    <property name="numeric">True</property>
+                    <property name="update_policy">if-valid</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkDrawingArea" id="flicker_marker">
+                <property name="visible">True</property>
+                <property name="app_paintable">True</property>
+                <property name="can_focus">False</property>
                 <property name="hexpand">True</property>
                 <property name="vexpand">True</property>
-                <property name="icon_name">image-missing</property>
-                <property name="icon_size">6</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -649,6 +725,48 @@
                 <property name="position">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkImage" id="optical_challenge">
+                    <property name="name">optical_challenge</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="icon_name">image-missing</property>
+                    <property name="icon_size">6</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkDrawingArea" id="flicker_challenge">
+                    <property name="visible">True</property>
+                    <property name="app_paintable">True</property>
+                    <property name="tooltip_text" translatable="yes" comments="TAN generator with flicker interface common in DE only">Hold the TAN generator in front of the animated graphic. The markings (triangles) on the graphic must match those on the TAN generator.</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                  </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>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
             <child>
               <object class="GtkGrid" id="grid1">
                 <property name="visible">True</property>
@@ -736,7 +854,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">True</property>
-                <property name="position">3</property>
+                <property name="position">4</property>
               </packing>
             </child>
           </object>
diff --git a/gnucash/import-export/aqb/gnc-flicker-gui.c b/gnucash/import-export/aqb/gnc-flicker-gui.c
new file mode 100644
index 000000000..b8bb981e5
--- /dev/null
+++ b/gnucash/import-export/aqb/gnc-flicker-gui.c
@@ -0,0 +1,445 @@
+/*
+ * gnc-flicker-gui.c --
+ *
+ * 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, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+/**
+ * @internal
+ * @file gnc-flicker-gui.c
+ * @brief GUI callbacks for Flicker and ChipTAN(optisch)
+ * @author Copyright (C) 2020 Christian Wehling <christian.wehling at web.de>
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "dialog-utils.h"
+#include "gnc-flicker-gui.h"
+#include "gnc-state.h"
+#include "gnc-ui.h"
+
+#define GNC_PREFS_GROUP     "dialogs.flicker"
+#define GNC_STATE_SECTION   "Flicker"
+#define STATE_KEY_BAR_WIDTH "barwidth"
+#define STATE_KEY_DELAY     "delay"
+
+#define BAR_WIDTH  44    /* Width of the flicker bars */
+#define BAR_HEIGHT 200   /* Height of the flicker bars */
+#define MARGIN     12    /* Distance between the flicker bars */
+#define DELAY      50    /* Pause between the flickering painting */
+
+static char *flicker_data (char const *challenge);
+static gboolean time_handler (GtkWidget *widget);
+static void do_marker_drawing (cairo_t *cr);
+static void draw_bit (cairo_t *cr, _Bool bit, int i);
+static void do_flicker_drawing (GtkWidget *widget, cairo_t *cr);
+static void do_flicker_load_state (GtkWidget *dialog);
+static void do_flicker_store_state (GtkWidget *dialog);
+static gboolean on_flicker_challenge_draw (GtkWidget *widget, cairo_t *cr,
+                                           gpointer user_data);
+static void on_flicker_challenge_map (GtkWidget *widget);
+static void on_flicker_challenge_destroy (GtkWidget *widget, gpointer user_data);
+static gboolean on_flicker_marker_draw (GtkWidget *widget, cairo_t *cr,
+                                        gpointer user_data);
+static void on_flicker_marker_map (GtkWidget *widget);
+static void on_spin_barwidth_value_changed (GtkSpinButton *spin, GtkWidget *widget);
+static void on_spin_delay_value_changed (GtkSpinButton *spin, GtkWidget *widget);
+static void on_dialog_destroy (GtkWidget *dialog, gpointer user_data);
+
+/* structured data for the flicker variables */
+struct Flickerdraw {
+    const char *challenge;
+    guint challenge_length;
+    guint margin;           /* Distance between bars */
+    guint barwidth;         /* Bar width */
+    guint barheight;        /* Bar height */
+    guint x_barpos;         /* x-value for the position of the bar */
+    guint y_barpos;         /* y-value for the position of the bar */
+    guint x_drawpos;        /* x-value of the first painting position */
+    guint y_drawpos;        /* y-value of the first painting position */
+    guint height;           /* Height of the drawing area */
+    guint width;            /* Width of the drawing area */
+    guint delay;            /* Waiting time between frames in milliseconds */
+    guint halfbyteid;
+    guint clock;
+    guint interval;
+    gboolean change_interval;
+};
+static struct Flickerdraw flickerdraw;
+
+static GncFlickerGui *flickergui = NULL;
+static _Bool bitarray[255][5];
+
+/* this function will return number corresponding 0,1,2..,9,A,B,C,D,E,F */
+static uint
+get_num (const char ch)
+{
+    int num =0;
+    if (ch >= '0' && ch <= '9')
+        num = ch - 0x30;
+    else
+    {
+        switch (ch)
+        {
+            case 'A': case 'a': num = 10; break;
+            case 'B': case 'b': num = 11; break;
+            case 'C': case 'c': num = 12; break;
+            case 'D': case 'd': num = 13; break;
+            case 'E': case 'e': num = 14; break;
+            case 'F': case 'f': num = 15; break;
+            default: num = 0;
+        }
+    }
+    /* The bank challenge has been verified by Aqbanking,
+     * so I assume that no error can occur. */
+    return num;
+}
+
+/* convert the bank challenge into the 5 bits for the flicker data */
+static char
+*flicker_data (const char *challenge)
+{
+    /* bitfield is a clock bit and a 4-bit code with the bits reversed
+       (bit 1 is the least significant and bit 4 the most
+       so 0x1 is 1000 and 0x8 is 0001) */
+     static const _Bool bits[16][5] =
+     {
+           {0, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, 0, 1, 0, 0}, {0, 1, 1, 0, 0},
+           {0, 0, 0, 1, 0}, {0, 1, 0, 1, 0}, {0, 0, 1, 1, 0}, {0, 1, 1, 1, 0},
+           {0, 0, 0, 0, 1}, {0, 1, 0, 0, 1}, {0, 0, 1, 0, 1}, {0, 1, 1, 0, 1},
+           {0, 0, 0, 1, 1}, {0, 1, 0, 1, 1}, {0, 0, 1, 1, 1}, {0, 1, 1, 1, 1}
+     };
+
+     /* prepend synchronization identifier */
+     const char pre[] = {'0', 'F', 'F', 'F'};
+     size_t len = sizeof (pre) + strlen (challenge) + 1;
+     char* code = (char*)malloc (len);
+     memcpy (code, pre, sizeof (pre));
+     memcpy (code + sizeof (pre), challenge, strlen (challenge));
+
+    /* Swap the position of the bits in pairs throughout the bank challenge
+       (low-order nibble first). */
+     for (uint i = 0; i < len - 1; i += 2)
+     {
+         u_int val1 = get_num (code[i]);
+         u_int val2 = get_num (code[i+1]);
+
+         memcpy (&bitarray[i], bits[val2], sizeof(bits[val2]));
+         memcpy (&bitarray[i+1], bits[val1], sizeof(bits[val1]));
+     }
+     return code;
+}
+
+/* A timer for redrawing the flickering painting, is started here and
+ * called up again when the "Delay" value is changed */
+static gboolean
+time_handler (GtkWidget *widget)
+{
+    /* Change of waiting time */
+    if (flickerdraw.change_interval)
+    {
+        g_source_remove (flickerdraw.interval);
+        flickerdraw.interval = g_timeout_add (flickerdraw.delay,
+                                              (GSourceFunc) time_handler,
+                                              (gpointer) widget);
+        flickerdraw.change_interval = FALSE;
+        return FALSE;
+    }
+    gtk_widget_queue_draw (widget);
+
+    return TRUE;
+}
+
+/* Show the colored triangle as a pointer for the position of the TAN generator */
+static void
+do_marker_drawing (cairo_t *cr)
+{
+    guint pos1;
+    guint pos2;
+
+    /* Initialize the drawing area to black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.9, 0.1, 0.1);
+    /* draw the left triangle */
+    pos1 = flickerdraw.x_drawpos + flickerdraw.barwidth / 2;
+    cairo_move_to (cr, pos1, 20);
+    cairo_line_to (cr, pos1 + 10, 2);
+    cairo_line_to (cr, pos1 - 10, 2);
+    cairo_close_path (cr);
+    cairo_stroke_preserve (cr);
+    cairo_fill (cr);
+
+    /* draw the right triangle */
+    pos2 = flickerdraw.x_drawpos + 4 * flickerdraw.margin + 4 * flickerdraw.barwidth +
+           flickerdraw.barwidth / 2;
+    cairo_move_to (cr, pos2, 20);
+    cairo_line_to (cr, pos2 + 10, 2);
+    cairo_line_to (cr, pos2 - 10, 2);
+    cairo_close_path (cr);
+    cairo_stroke_preserve (cr);
+    cairo_fill (cr);
+}
+
+/* draws the 5 flickering bars of the bank data into the drawing area */
+static void
+draw_bit (cairo_t *cr, _Bool bit, int i)
+{
+    if (bit & 1)
+        cairo_set_source_rgb (cr, 1, 1, 1);
+    else
+        cairo_set_source_rgb (cr, 0, 0, 0);
+
+    flickerdraw.x_barpos = flickerdraw.x_drawpos + i * flickerdraw.margin +
+                           i * flickerdraw.barwidth;
+    cairo_rectangle (cr, flickerdraw.x_barpos, flickerdraw.y_barpos,
+                     flickerdraw.barwidth, flickerdraw.barheight);
+    cairo_fill (cr);
+}
+
+/* Prepares the drawing area for the flicker graphic. */
+static void
+do_flicker_drawing (GtkWidget *widget, cairo_t *cr)
+{
+    /* Always align the flicker display in the middle of the drawing area */
+    flickerdraw.width = gtk_widget_get_allocated_width (widget);
+
+    /* Start position of the first bar */
+    flickerdraw.x_drawpos = (flickerdraw.width - 4 * flickerdraw.margin -
+                             5 * flickerdraw.barwidth) / 2;
+
+    /* Initialize the drawing area to black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    /* paint the flicker graphic */
+    bitarray[flickerdraw.halfbyteid][0] = flickerdraw.clock;
+    draw_bit (cr, flickerdraw.clock, 0);
+    for (int i = 1; i <= 4; i++)
+        draw_bit (cr, bitarray[flickerdraw.halfbyteid][i], i);
+
+    /* Each flicker point is drawn twice. Once with clock = 1 and once with clock = 0 */
+    if (!flickerdraw.clock)
+    {
+        flickerdraw.clock = 1;
+        flickerdraw.halfbyteid++;
+        if (flickerdraw.halfbyteid >= flickerdraw.challenge_length)
+            flickerdraw.halfbyteid = 0;
+    }
+    else if (flickerdraw.clock)
+        flickerdraw.clock = 0;
+}
+
+/* Load the state of the GUI (Size of the Dialog, Value of the Spinbutton)  */
+static void
+do_flicker_load_state (GtkWidget *dialog)
+{
+    /* Load the values in the spin button */
+    GKeyFile *state_file = gnc_state_get_current ();
+
+    if (g_key_file_has_key (state_file, GNC_STATE_SECTION, STATE_KEY_BAR_WIDTH, NULL))
+        flickerdraw.barwidth = g_key_file_get_integer (state_file,
+                                                       GNC_STATE_SECTION,
+                                                       STATE_KEY_BAR_WIDTH, NULL);
+    else
+        flickerdraw.barwidth = BAR_WIDTH;
+
+    if (g_key_file_has_key (state_file, GNC_STATE_SECTION, STATE_KEY_DELAY, NULL))
+        flickerdraw.delay = g_key_file_get_integer (state_file,
+                                                    GNC_STATE_SECTION,
+                                                    STATE_KEY_DELAY, NULL);
+    else
+        flickerdraw.delay = DELAY;
+
+    /* Load window size and position */
+    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW (dialog),
+                             gnc_ui_get_main_window (NULL));
+}
+
+/* Stores the state of the GUI (Size of the Dialog, Value of the Spinbutton) */
+static void
+do_flicker_store_state (GtkWidget *dialog)
+{
+    /* Save the values in the spin button */
+    GKeyFile *state_file = gnc_state_get_current ();
+
+    if (flickerdraw.barwidth != BAR_WIDTH)
+        g_key_file_set_integer (state_file, GNC_STATE_SECTION,
+                                STATE_KEY_BAR_WIDTH, flickerdraw.barwidth);
+    else if (g_key_file_has_key (state_file, GNC_STATE_SECTION,
+                                 STATE_KEY_BAR_WIDTH, NULL))
+        g_key_file_remove_key (state_file, GNC_STATE_SECTION,
+                               STATE_KEY_BAR_WIDTH, NULL);
+
+    if (flickerdraw.delay != DELAY)
+        g_key_file_set_integer (state_file, GNC_STATE_SECTION,
+                                STATE_KEY_DELAY, flickerdraw.delay);
+    else if (g_key_file_has_key (state_file, GNC_STATE_SECTION,
+                                 STATE_KEY_DELAY, NULL))
+        g_key_file_remove_key (state_file, GNC_STATE_SECTION,
+                               STATE_KEY_DELAY, NULL);
+
+    /* Save window size and position */
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW (dialog));
+}
+
+/* This signal is emitted when the drawing area "flicker challenge" is visible */
+static void
+on_flicker_challenge_map (GtkWidget *widget)
+{
+    gchar *code = g_malloc (strlen (flickerdraw.challenge) + 4);
+    code = flicker_data (flickerdraw.challenge);
+    flickerdraw.challenge_length = strlen (code);
+
+    /* Set the height of the drawing area */
+    flickerdraw.height = flickerdraw.barheight + 2 * flickerdraw.y_barpos;
+    gtk_widget_set_size_request (widget, -1, flickerdraw.height);
+
+    /* Call up the time function and start the flicker display */
+    flickerdraw.interval = g_timeout_add (flickerdraw.delay,
+                                          (GSourceFunc) time_handler,
+                                          (gpointer) widget);
+}
+
+/* Initialize the drawingarea to black and paint the flickerchallenge */
+static gboolean
+on_flicker_challenge_draw (GtkWidget *widget, cairo_t *cr,
+                           __attribute__((unused)) gpointer user_data)
+{
+    do_flicker_drawing (widget, cr);
+
+    return FALSE;
+}
+
+/* called when the drawing area is destroyed */
+static void
+on_flicker_challenge_destroy (GtkWidget *widget,
+                              __attribute__((unused)) gpointer user_data)
+{
+    /* remove the timeout function */
+    g_source_remove (flickerdraw.interval);
+}
+
+/* Initialize the drawing area "flicker marker" in black and draw the marker for
+ * the position of the TAN-Generator */
+static gboolean
+on_flicker_marker_draw (__attribute__((unused)) GtkWidget *widget, cairo_t *cr,
+                        __attribute__((unused)) gpointer data)
+{
+    do_marker_drawing (cr);
+
+    return FALSE;
+}
+
+/* This signal is emitted when the drawing area "flicker marker" is visible */
+static void
+on_flicker_marker_map (GtkWidget *widget)
+{
+    /* Set the height of the drawing area */
+    gtk_widget_set_size_request (widget, -1, flickerdraw.y_barpos);
+}
+
+/* The value for "Field width" has been changed on the spin button and the
+ * flicker display is updated */
+static void
+on_spin_barwidth_value_changed (GtkSpinButton *spin, GtkWidget *widget)
+{
+    flickerdraw.barwidth = gtk_spin_button_get_value_as_int (spin);
+    flickerdraw.x_drawpos = (flickerdraw.width - 4 * flickerdraw.margin -
+                             5 * flickerdraw.barwidth) / 2;
+
+    /* Moving the position triangles */
+    gtk_widget_queue_draw (widget);
+}
+
+/* The value for "waiting time" was changed on the spin button and
+ * the speed of the flickering display is updated */
+static void
+on_spin_delay_value_changed (GtkSpinButton *spin, GtkWidget *widget)
+{
+    flickerdraw.delay = gtk_spin_button_get_value_as_int (spin);
+
+    flickerdraw.change_interval = TRUE;
+    time_handler (widget);
+}
+
+static void
+on_dialog_destroy (GtkWidget *dialog, __attribute__((unused)) gpointer user_data)
+{
+    /* Store window size and initial setting values */
+    do_flicker_store_state (dialog);
+}
+
+/* The widgets for the GUI are prepared and the first parameters are set  */
+void
+ini_flicker_gui (const char *pChallenge, GncFlickerGui *gui)
+{
+    /* Establish reference to the dialog widgets created in gnc_gwen_gui */
+    flickergui = gui;
+
+    /* Load window size and initial setting values */
+    do_flicker_load_state (GTK_WIDGET (flickergui->dialog));
+
+    /* Initialize application */
+    flickerdraw.barheight = BAR_HEIGHT;
+    flickerdraw.margin = MARGIN;
+    flickerdraw.y_barpos = 20;        /* First painting position */
+    flickerdraw.halfbyteid = 0;
+    flickerdraw.clock = 1;
+
+    flickerdraw.challenge = pChallenge;
+
+    g_signal_connect (GTK_WINDOW (flickergui->dialog), "destroy",
+                      G_CALLBACK (on_dialog_destroy), NULL);
+
+    g_signal_connect (GTK_WIDGET (flickergui->flicker_challenge), "map",
+                      G_CALLBACK (on_flicker_challenge_map), NULL);
+    g_signal_connect (GTK_WIDGET (flickergui->flicker_challenge), "draw",
+                      G_CALLBACK (on_flicker_challenge_draw), NULL);
+    g_signal_connect (GTK_WIDGET (flickergui->flicker_challenge), "destroy",
+                      G_CALLBACK (on_flicker_challenge_destroy), NULL);
+
+    g_signal_connect (GTK_WIDGET (flickergui->flicker_marker), "map",
+                      G_CALLBACK (on_flicker_marker_map), NULL);
+    g_signal_connect (GTK_WIDGET (flickergui->flicker_marker), "draw",
+                      G_CALLBACK (on_flicker_marker_draw), NULL);
+
+    flickergui->adj_barwidth = gtk_adjustment_new (0.0, 10.0, 80.0, 1.0, 10.0, 0.0);
+    gtk_spin_button_set_adjustment (flickergui->spin_barwidth, flickergui->adj_barwidth);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON (flickergui->spin_barwidth),
+                               flickerdraw.barwidth);
+    g_signal_connect (GTK_WIDGET (flickergui->spin_barwidth), "value-changed",
+                      G_CALLBACK (on_spin_barwidth_value_changed),
+                                 flickergui->flicker_marker);
+    gtk_widget_set_focus_on_click (GTK_WIDGET (flickergui->spin_barwidth), FALSE);
+
+    flickergui->adj_delay = gtk_adjustment_new (0.0, 10.0, 100.0, 10.0, 10.0, 0.0);
+    gtk_spin_button_set_adjustment (flickergui->spin_delay, flickergui->adj_delay);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON (flickergui->spin_delay),
+                               flickerdraw.delay);
+    g_signal_connect (GTK_WIDGET (flickergui->spin_delay), "value-changed",
+                      G_CALLBACK (on_spin_delay_value_changed),
+                      flickergui->flicker_challenge);
+    gtk_widget_set_focus_on_click (GTK_WIDGET (flickergui->spin_delay), FALSE);
+
+    gtk_widget_grab_focus (GTK_WIDGET (flickergui->input_entry));
+}
diff --git a/gnucash/import-export/aqb/gnc-flicker-gui.h b/gnucash/import-export/aqb/gnc-flicker-gui.h
new file mode 100644
index 000000000..bf1a7cd2b
--- /dev/null
+++ b/gnucash/import-export/aqb/gnc-flicker-gui.h
@@ -0,0 +1,63 @@
+/*
+ * gnc-flicker-gui.h
+ *
+ * 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, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+/**
+ * @addtogroup Import_Export
+ * @{
+ * @addtogroup AqBanking
+ * @{
+ * @file gnc-flicker-gui.h
+ * @brief GUI callbacks for Flicker and ChipTAN(optisch)
+ * @author Copyright (C) 2020 Christian Wehling <christian.wehling at web.de>
+ */
+
+#ifndef GNC_FLICKER_GUI_H
+#define GNC_FLICKER_GUI_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+// structured data for the GUI
+typedef struct
+{
+    GtkWidget     *dialog;
+    GtkWidget     *input_entry;
+    GtkWidget     *flicker_challenge;
+    GtkWidget     *flicker_marker;
+    GtkWidget     *flicker_hbox;
+    GtkAdjustment *adj_barwidth;
+    GtkAdjustment *adj_delay;
+    GtkSpinButton *spin_barwidth;
+    GtkSpinButton *spin_delay;
+} GncFlickerGui;
+
+/**
+ * Initialize the dialog and drawing area
+ *
+ * @param pChallenge: The answer from the bank which is shown as a flickering picture
+ * @param gui: The structure of the Dialog-Widgets
+ */
+void ini_flicker_gui (const char *pChallenge, GncFlickerGui *gui);
+
+G_END_DECLS
+
+#endif /* GNC_FLICKER_GUI_H */
diff --git a/gnucash/import-export/aqb/gnc-gwen-gui.c b/gnucash/import-export/aqb/gnc-gwen-gui.c
index 54fa72953..aeb70b481 100644
--- a/gnucash/import-export/aqb/gnc-gwen-gui.c
+++ b/gnucash/import-export/aqb/gnc-gwen-gui.c
@@ -46,6 +46,8 @@
 #include "gnc-plugin-aqbanking.h"
 #include "qof.h"
 
+#include "gnc-flicker-gui.h"
+
 # define GNC_GWENHYWFAR_CB GWENHYWFAR_CB
 
 #define GWEN_GUI_CM_CLASS "dialog-hbcilog"
@@ -928,6 +930,9 @@ get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
     GtkWidget *confirm_label;
     GtkWidget *remember_pin_checkbutton;
     GtkImage *optical_challenge;
+
+    static GncFlickerGui *flickergui = NULL;
+
     const gchar *internal_input, *internal_confirmed;
     gboolean confirm = (flags & GWEN_GUI_INPUT_FLAGS_CONFIRM) != 0;
     gboolean is_tan = (flags & GWEN_GUI_INPUT_FLAGS_TAN) != 0;
@@ -949,9 +954,33 @@ get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
     remember_pin_checkbutton = GTK_WIDGET(gtk_builder_get_object (builder, "remember_pin"));
     optical_challenge = GTK_IMAGE(gtk_builder_get_object (builder, "optical_challenge"));
     gtk_widget_set_visible(GTK_WIDGET(optical_challenge), FALSE);
+
+    flickergui = g_slice_new(GncFlickerGui);
+    flickergui->flicker_challenge = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_challenge"));
+    flickergui->flicker_marker = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_marker"));
+    flickergui->flicker_hbox = GTK_WIDGET(gtk_builder_get_object(builder, "flicker_hbox"));
+    flickergui->spin_barwidth = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spin_barwidth"));
+    flickergui->spin_delay = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spin_delay"));
+
+    gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_challenge), FALSE);
+    gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_marker), FALSE);
+    gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_hbox), FALSE);
+    gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_barwidth), FALSE);
+    gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_delay), FALSE);
+
     #ifdef AQBANKING6
-    if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
+    if (g_strcmp0(mimeType,"text/x-flickercode") == 0 && pChallenge != NULL)
     {
+        /* Chiptan Optic (aka Flicker) */
+        gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_challenge), TRUE);
+        gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_marker), TRUE);
+        gtk_widget_set_visible(GTK_WIDGET(flickergui->flicker_hbox), TRUE);
+        gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_barwidth), TRUE);
+        gtk_widget_set_visible(GTK_WIDGET(flickergui->spin_delay), TRUE);
+    }
+    else if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
+    {
+        /* Phototan or Chiptan QR */
         gtk_widget_set_visible(GTK_WIDGET(optical_challenge), TRUE);
     }
     #endif
@@ -969,6 +998,7 @@ get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
     if ((flags & (GWEN_GUI_INPUT_FLAGS_TAN | GWEN_GUI_INPUT_FLAGS_SHOW)) != 0)
     {
         gtk_widget_set_visible(input_entry, TRUE);
+        gtk_entry_set_visibility(GTK_ENTRY(input_entry), TRUE);
     }
 
     if (gui->dialog)
@@ -996,6 +1026,7 @@ get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
     //if (optical_challenge)
     if(mimeType != NULL && pChallenge != NULL && lChallenge > 0)
     {
+        /* Phototan or Chiptan QR */
         // convert PNG and load into widget
         // TBD: check mimeType?
         guchar *gudata = (guchar*)pChallenge;
@@ -1019,6 +1050,15 @@ get_input(GncGWENGui *gui, guint32 flags, const gchar *title,
 
         gtk_image_set_from_pixbuf(optical_challenge, pixbuf);
     }
+    else if (g_strcmp0(mimeType,"text/x-flickercode") == 0 && pChallenge != NULL)
+    {
+        /* Chiptan Optic (aka Flicker) */
+        flickergui->dialog = dialog;
+        flickergui->input_entry = input_entry;
+
+        ini_flicker_gui(pChallenge, flickergui);
+        g_slice_free(GncFlickerGui, flickergui);
+    }
     #endif
 
     if (*input)
@@ -1425,28 +1465,43 @@ getpassword_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *token,
     if(is_tan && methodId == GWEN_Gui_PasswordMethod_OpticalHHD)
     {
         /**
-        * TODO: How to handle Flicker code (use WebView and JS???)
-        *
         * use GWEN_Gui_PasswordMethod_Mask to get the basic method id
         *  cf. gui/gui.h of gwenhywfar
         */
         opticalMethodId=GWEN_DB_GetIntValue(methodParams, "tanMethodId", 0, AB_BANKING_TANMETHOD_TEXT);
         switch(opticalMethodId)
         {
+            case AB_BANKING_TANMETHOD_CHIPTAN:
+                break;
+            case AB_BANKING_TANMETHOD_CHIPTAN_OPTIC:
+                mimeType = "text/x-flickercode";
+                pChallenge = GWEN_DB_GetCharValue(methodParams, "challenge", 0, NULL);
+                if ((pChallenge == NULL) || (pChallenge[0] == '\0'))
+                {
+                    /* empty flicker-data */
+                    return GWEN_ERROR_NO_DATA;
+                }
+                break;
+            case AB_BANKING_TANMETHOD_CHIPTAN_USB:
+                /**
+                 * ToDo: is this the same as CHIPTAN_OPTIC ?
+                 */
+                 break;
             case AB_BANKING_TANMETHOD_PHOTOTAN:
             case AB_BANKING_TANMETHOD_CHIPTAN_QR:
-            /**
-            * image data is in methodParams
-            */
-            mimeType=GWEN_DB_GetCharValue(methodParams, "mimeType", 0, NULL);
-            pChallenge=(const char*) GWEN_DB_GetBinValue(methodParams, "imageData", 0, NULL, 0, &lChallenge);
-            if (!(pChallenge && lChallenge)) {
-                /* empty optical data */
-                return GWEN_ERROR_NO_DATA;
-            }
-            break;
-        default:
-            break;
+                /**
+                 * image data is in methodParams
+                 */
+                mimeType=GWEN_DB_GetCharValue(methodParams, "mimeType", 0, NULL);
+                pChallenge=(const char*) GWEN_DB_GetBinValue(methodParams, "imageData", 0, NULL, 0, &lChallenge);
+                if (!(pChallenge && lChallenge))
+                {
+                    /* empty optical data */
+                    return GWEN_ERROR_NO_DATA;
+                }
+                break;
+            default:
+                break;
         }
     }
     #endif
diff --git a/gnucash/import-export/aqb/gschemas/CMakeLists.txt b/gnucash/import-export/aqb/gschemas/CMakeLists.txt
index ba5425e6b..6ded5fd8d 100644
--- a/gnucash/import-export/aqb/gschemas/CMakeLists.txt
+++ b/gnucash/import-export/aqb/gschemas/CMakeLists.txt
@@ -1,8 +1,8 @@
 
 if (WITH_AQBANKING)
-  set(aqb_GSCHEMA org.gnucash.dialogs.import.hbci.gschema.xml)
+  set(aqb_GSCHEMA org.gnucash.dialogs.import.hbci.gschema.xml org.gnucash.dialogs.flicker.gschema.xml)
 
   add_gschema_targets("${aqb_GSCHEMA}")
 endif()
 
-set_dist_list(aqbanking_gschema_DIST CMakeLists.txt org.gnucash.dialogs.import.hbci.gschema.xml.in)
+set_dist_list(aqbanking_gschema_DIST CMakeLists.txt org.gnucash.dialogs.import.hbci.gschema.xml.in org.gnucash.dialogs.flicker.gschema.xml.in)
diff --git a/gnucash/import-export/aqb/gschemas/org.gnucash.dialogs.flicker.gschema.xml.in b/gnucash/import-export/aqb/gschemas/org.gnucash.dialogs.flicker.gschema.xml.in
new file mode 100644
index 000000000..d8e183f97
--- /dev/null
+++ b/gnucash/import-export/aqb/gschemas/org.gnucash.dialogs.flicker.gschema.xml.in
@@ -0,0 +1,13 @@
+<schemalist gettext-domain="@PROJECT_NAME@">
+
+  <schema id="org.gnucash.dialogs.flicker" path="/org/gnucash/dialogs/flicker/">
+    <key name="last-geometry" type="(iiii)">
+      <default>(-1,-1,-1,-1)</default>
+      <summary>Last window position and size</summary>
+      <description>This setting describes the size and position of the window when it was last closed.
+        The numbers are the X and Y coordinates of the top left corner of the window
+        followed by the width and height of the window.</description>
+    </key>
+  </schema>
+
+</schemalist>



Summary of changes:
 gnucash/import-export/aqb/CMakeLists.txt           |   2 +
 gnucash/import-export/aqb/dialog-ab.glade          | 128 +++++-
 gnucash/import-export/aqb/gnc-flicker-gui.c        | 445 +++++++++++++++++++++
 .../aqb/{gnc-ab-transfer.h => gnc-flicker-gui.h}   |  45 ++-
 gnucash/import-export/aqb/gnc-gwen-gui.c           | 107 ++++-
 gnucash/import-export/aqb/gschemas/CMakeLists.txt  |   4 +-
 .../org.gnucash.dialogs.flicker.gschema.xml.in}    |   9 +-
 po/POTFILES.in                                     |   2 +
 8 files changed, 691 insertions(+), 51 deletions(-)
 create mode 100644 gnucash/import-export/aqb/gnc-flicker-gui.c
 copy gnucash/import-export/aqb/{gnc-ab-transfer.h => gnc-flicker-gui.h} (52%)
 copy gnucash/{gschemas/org.gnucash.dialogs.commodities.gschema.xml.in => import-export/aqb/gschemas/org.gnucash.dialogs.flicker.gschema.xml.in} (62%)



More information about the gnucash-changes mailing list