r15911 - gnucash/trunk/src/gnome-utils - Fix quickfill handling in transfer dialogs, bug #413868.

Andreas Köhler andi5 at cvs.gnucash.org
Mon Apr 16 16:51:40 EDT 2007


Author: andi5
Date: 2007-04-16 16:51:37 -0400 (Mon, 16 Apr 2007)
New Revision: 15911
Trac: http://svn.gnucash.org/trac/changeset/15911

Modified:
   gnucash/trunk/src/gnome-utils/dialog-transfer.c
Log:
Fix quickfill handling in transfer dialogs, bug #413868.

Connect to key_press_event on description entry before the default
handler, let RET act like OK.  Do not care about button releases.  On
insert_text, make quickfill as in the register, set the position and
g_idle_add a function to set the selected region.


Modified: gnucash/trunk/src/gnome-utils/dialog-transfer.c
===================================================================
--- gnucash/trunk/src/gnome-utils/dialog-transfer.c	2007-04-16 18:11:07 UTC (rev 15910)
+++ gnucash/trunk/src/gnome-utils/dialog-transfer.c	2007-04-16 20:51:37 UTC (rev 15911)
@@ -85,11 +85,10 @@
 
   XferDirection quickfill;	/* direction match on the account instead. */
 
-  /* stored data for the description quickfill functionality */
+  /* stored data for the description quickfill selection function */
   gint desc_start_selection;
   gint desc_end_selection;
-  gint desc_cursor_position;
-  gboolean desc_didquickfill;
+  guint desc_selection_source_id;
 
   GtkWidget * transferinfo_label;
 
@@ -644,154 +643,86 @@
   return( changed );
 }
 
-/* The insert_cb will do the insert and quickfill if possible, but won't
- * set the selection or cursor position since the entry widget seems to
- * change these itself.  Instead, a flag will be set and either the
- * key_press_cb or the button_release_cb will set the selection and
- * cursor position stored off from the insert_cb.
+static gboolean
+idle_select_region(gpointer data)
+{
+  XferDialog *xferData = data;
+  g_return_val_if_fail(xferData, FALSE);
+
+  gtk_editable_select_region(GTK_EDITABLE(xferData->description_entry),
+			     xferData->desc_start_selection,
+			     xferData->desc_end_selection);
+
+  xferData->desc_selection_source_id = 0;
+  return FALSE;
+}
+
+/* The insert_cb will do the insert and quickfill if possible and set the
+ * cursor position accordingly.  It will not set the selection but will register
+ * idle_select_region to do that once the program returns to its main loop.
  */
 static void
-gnc_xfer_description_insert_cb(GtkEntry *entry,
+gnc_xfer_description_insert_cb(GtkEditable *editable,
                                const gchar *insert_text,
                                const gint insert_text_len,
                                gint *start_pos,
                                XferDialog *xferData)
 {
-  GString *change_text_gs, *new_text_gs;
-  glong old_text_chars, new_text_chars;
-  const char *old_text, *match_str = NULL;
+  gchar *prefix, *suffix, *new_text;
   QuickFill *match;
-  int i;
-  const char *c;
-  gunichar uc;
+  const gchar *match_str;
+  gint prefix_len, new_text_len, match_str_len;
 
-  xferData->desc_didquickfill = FALSE;
-
-  if ( insert_text_len <= 0 )
+  if (insert_text_len <= 0)
     return;
 
-  old_text = gtk_entry_get_text (entry);
-  if (!old_text)
-    old_text = "";
+  suffix = gtk_editable_get_chars(editable, *start_pos, -1);
 
   /* If we are inserting in the middle, do nothing */
-  old_text_chars = g_utf8_strlen (old_text, -1);
-  if( *start_pos < old_text_chars )
+  if (*suffix) {
+    g_free(suffix);
     return;
-
-  change_text_gs = g_string_new_len (insert_text, insert_text_len);
-
-  /* Construct what the new value of the text entry will be */
-  new_text_gs = g_string_new ("");
-  
-  i = 0;
-  c = old_text;
-  //Copy old text up to insert position
-  while ( *c && ( i < *start_pos ) )
-  {
-    uc = g_utf8_get_char ( c );
-    g_string_append_unichar ( new_text_gs, uc );
-    c = g_utf8_next_char ( c );
-    i++;      
   }
+  g_free(suffix);
 
-  //Copy inserted text
-  g_string_append ( new_text_gs, change_text_gs->str );
+  prefix = gtk_editable_get_chars(editable, 0, *start_pos);
+  new_text = g_strconcat(prefix, insert_text, (gchar*) NULL);
+  prefix_len = strlen(prefix);
+  new_text_len = prefix_len + insert_text_len;
+  g_free(prefix);
 
-  //Copy old text after insert position
-  while ( *c )
+  if ((match = gnc_quickfill_get_string_match(xferData->qf, new_text))
+      && (match_str = gnc_quickfill_string(match))
+      && ((match_str_len = strlen(match_str)) > new_text_len))
   {
-    uc = g_utf8_get_char ( c );
-    g_string_append_unichar ( new_text_gs, uc );
-    c = g_utf8_next_char ( c );
-  }
-
-  if( ( match = gnc_quickfill_get_string_match( xferData->qf, new_text_gs->str ) )
-   && ( match_str = gnc_quickfill_string( match ) ) 
-   && safe_strcmp( new_text_gs->str, old_text ) )
-  {
-    g_signal_handlers_block_matched (G_OBJECT (entry),
+    g_signal_handlers_block_matched (G_OBJECT (editable),
 				     G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xferData);
 
-    gtk_entry_set_text( entry, match_str );
+    gtk_editable_insert_text(editable,
+			     match_str + prefix_len,
+			     match_str_len - prefix_len,
+			     start_pos);
 
-    g_signal_handlers_unblock_matched (G_OBJECT (entry),
+    g_signal_handlers_unblock_matched (G_OBJECT (editable),
 				       G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xferData);
 
     /* stop the current insert */
-    g_signal_stop_emission_by_name (G_OBJECT (entry), "insert_text");
+    g_signal_stop_emission_by_name (G_OBJECT (editable), "insert_text");
 
-    /* This doesn't seem to fix the selection problems, why? */
-    gtk_editable_select_region (GTK_EDITABLE(entry), 0, 0);
+    /* set the position */
+    *start_pos = g_utf8_strlen(new_text, -1);
 
-    /* Store off data for the key_press_cb or
-     * the button_release_cb to make use of. */
-    new_text_chars = g_utf8_strlen (new_text_gs->str, -1);
-    xferData->desc_cursor_position = new_text_chars;
-    xferData->desc_start_selection = new_text_chars;
+    /* select region on idle, because it would be reset once this function
+       finishes */
+    xferData->desc_start_selection = *start_pos;
     xferData->desc_end_selection = -1;
-    xferData->desc_didquickfill = TRUE;
+    xferData->desc_selection_source_id = g_idle_add(idle_select_region,
+						    xferData);
   }
-
-  g_string_free (change_text_gs, TRUE);
-  g_string_free (new_text_gs, TRUE);
-  
+  g_free(new_text);
 }
 
-/* This common post-key press and post-button release handler fixes
- * up the selection and cursor position changes that may be necessary
- * if a quickfill occurred in the insert_cb.
- */
 static gboolean
-common_post_quickfill_handler(guint32 time, XferDialog *xferData )
-{
-  GtkEntry *entry = GTK_ENTRY(xferData->description_entry);
-  gint current_pos;
-  gint current_start;
-  gint current_end;
-  gboolean did_something = FALSE;   /* was the selection or position changed? */
-
-  ENTER(" ");
-  current_pos = gtk_editable_get_position( GTK_EDITABLE(entry) );
-  gtk_editable_get_selection_bounds( GTK_EDITABLE(entry),
-				     &current_start,
-				     &current_end);
-  if( current_pos != xferData->desc_cursor_position )
-  {
-    gtk_editable_set_position( GTK_EDITABLE(entry),
-			       xferData->desc_cursor_position );
-    did_something = TRUE;
-  }
-
-  if( ( current_start != xferData->desc_start_selection ||
-        current_end   != xferData->desc_end_selection      ) &&
-      ( xferData->desc_start_selection != xferData->desc_end_selection ||
-        xferData->desc_start_selection == 0 ) )
-  {
-    gtk_editable_select_region( GTK_EDITABLE(entry),
-				xferData->desc_start_selection,
-				xferData->desc_end_selection );
-    did_something = TRUE;
-  }
-
-  if( did_something ) 
-  {
-    /* Make sure we don't try to change things again based on these values. */
-    xferData->desc_start_selection = current_start;
-    xferData->desc_end_selection = current_end;
-    xferData->desc_cursor_position = current_pos;
-  }
-
-  /* Make sure a new quickfill must occur before coming back through here,
-   * whether or not we actually did anything in this function.
-   */
-  xferData->desc_didquickfill = FALSE;
-
-  LEAVE("did_something=%d", did_something);
-  return( did_something );
-}
-
-static gboolean
 gnc_xfer_description_key_press_cb( GtkEntry *entry,
                                    GdkEventKey *event,
                                    XferDialog *xferData )
@@ -800,27 +731,15 @@
 
   /* Most "special" keys are allowed to be handled directly by
    * the entry's key press handler, but in some cases that doesn't
-   * seem to work right, so handle it here.
+   * seem to work right, so handle them here.
    */
   ENTER(" ");
   switch( event->keyval )
   {
-    case GDK_Left:        /* right/left cause a focus change which is bad */
-    case GDK_KP_Left:
-    case GDK_Right:
-    case GDK_KP_Right:
-      done_with_input = TRUE;
-      break;
-
-    case GDK_Return:      /* On the first activate, need to
-                           * do the quickfill completion */
+    case GDK_Return:
     case GDK_KP_Enter:
-      if( gnc_xfer_dialog_quickfill( xferData ) )
-        done_with_input = TRUE;
-      /* Else if no updates were done, allow the keypress to go through,
-       * which will result in an activate signal for the dialog.
-       */
-
+      gnc_xfer_dialog_quickfill( xferData );
+      /* NOT done with input, activate the default button of the dialog. */
       break;
 
     case GDK_Tab:
@@ -838,35 +757,10 @@
       break;
   }
 
-  /* Common handling for both key presses and button releases
-   * to fix up the selection and cursor position at this point.
-   */
-  if( !done_with_input && xferData->desc_didquickfill )
-    done_with_input = common_post_quickfill_handler( event->time, xferData );
-
-  if( done_with_input )
-    g_signal_stop_emission_by_name (G_OBJECT (entry), "key_press_event");
-
   LEAVE("done=%d", done_with_input);
   return( done_with_input );
 }
 
-static gboolean
-gnc_xfer_description_button_release_cb( GtkEntry *entry,
-                                        GdkEventButton *event,
-                                        XferDialog *xferData )
-{
-  if( xferData->desc_didquickfill )
-  {
-    /* Common handling for both key presses and button presses
-     * to fix up the selection and cursor position at this point.
-     */
-    common_post_quickfill_handler( event->time, xferData );
-  }
-
-  return( FALSE );
-}
-
 /*** End of quickfill-specific callbacks ***/
 
 static void
@@ -1622,6 +1516,9 @@
   gnc_quickfill_destroy (xferData->qf);
   xferData->qf = NULL;
 
+  if (xferData->desc_selection_source_id)
+    g_source_remove (xferData->desc_selection_source_id);
+
   g_free(xferData);
 
   DEBUG("xfer dialog destroyed");
@@ -1705,10 +1602,8 @@
     /* Get signals from the Description for quickfill. */
     g_signal_connect (G_OBJECT (entry), "insert_text",
 		      G_CALLBACK (gnc_xfer_description_insert_cb), xferData);
-    g_signal_connect (G_OBJECT (entry), "button_release_event",
-		      G_CALLBACK (gnc_xfer_description_button_release_cb), xferData);
-    g_signal_connect_after (G_OBJECT (entry), "key_press_event",
-			    G_CALLBACK (gnc_xfer_description_key_press_cb), xferData);
+    g_signal_connect (G_OBJECT (entry), "key_press_event",
+		      G_CALLBACK (gnc_xfer_description_key_press_cb), xferData);
 
     entry = glade_xml_get_widget (xml, "memo_entry");
     xferData->memo_entry = entry;
@@ -1862,10 +1757,9 @@
 
   xferData = g_new0 (XferDialog, 1);
 
-  xferData->desc_cursor_position = 0;
   xferData->desc_start_selection = 0;
   xferData->desc_end_selection = 0;
-  xferData->desc_didquickfill = FALSE;
+  xferData->desc_selection_source_id = 0;
   xferData->quickfill = XFER_DIALOG_FROM;
   xferData->transaction_cb = NULL;
 



More information about the gnucash-changes mailing list